diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/fs/dquot.c linux-2.4.0-test11-newquota/fs/dquot.c
--- linux-2.4.0-test11-quotafix/fs/dquot.c	Mon Nov 27 09:43:43 2000
+++ linux-2.4.0-test11-newquota/fs/dquot.c	Mon Nov 27 10:08:00 2000
@@ -26,7 +26,7 @@
  *		dquot_incr_...() to calling functions.
  *		invalidate_dquots() now writes modified dquots.
  *		Serialized quota_off() and quota_on() for mount point.
- *		Fixed a few bugs in grow_dquots.
+ *		Fixed a few bugs in grow_dquots().
  *		Fixed deadlock in write_dquot() - we no longer account quotas on
  *		quota files
  *		remove_dquot_ref() moved to inode.c - it now traverses through inodes
@@ -42,6 +42,10 @@
  *		Write updated not to require dquot lock
  *		Jan Kara, <jack@suse.cz>, 9/2000
  *
+ *		New quotafile format
+ *		Alocation units changed to bytes
+ *		Jan Kara, <jack@suse.cz>, 2000
+ *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
 
@@ -60,14 +64,18 @@
 #include <linux/slab.h>
 
 #include <asm/uaccess.h>
+#include <asm/byteorder.h>
 
-#define __DQUOT_VERSION__	"dquot_6.4.0"
+#define __DQUOT_VERSION__	"dquot_6.5.0"
+#define __DQUOT_PARANOIA
 
 int nr_dquots, nr_free_dquots;
 
-static char *quotatypes[] = INITQFNAMES;
+static const char *quotatypes[] = INITQFNAMES;
+static const uint quota_magics[] = INITQMAGICS;
+static const uint quota_versions[] = INITQVERSIONS;
 
-static inline struct quota_mount_options *sb_dqopt(struct super_block *sb)
+static inline struct quota_info *sb_dqopt(struct super_block *sb)
 {
 	return &sb->s_dquot;
 }
@@ -107,7 +115,7 @@
 static void dqput(struct dquot *);
 static struct dquot *dqduplicate(struct dquot *);
 
-static inline char is_enabled(struct quota_mount_options *dqopt, short type)
+static inline char is_enabled(struct quota_info *dqopt, short type)
 {
 	switch (type) {
 		case USRQUOTA:
@@ -176,11 +184,12 @@
 
 static inline void remove_free_dquot(struct dquot *dquot)
 {
-	/* sanity check */
+#ifdef __DQUOT_PARANOIA
 	if (list_empty(&dquot->dq_free)) {
 		printk("remove_free_dquot: dquot not on the free list??\n");
 		return;		/* J.K. Just don't do anything */
 	}
+#endif
 	list_del(&dquot->dq_free);
 	INIT_LIST_HEAD(&dquot->dq_free);
 	nr_free_dquots--;
@@ -249,6 +258,378 @@
 }
 
 /*
+ *	IO operations on file
+ */
+
+#define GETIDINDEX(id, depth) (((id) >> ((DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define GETENTRIES(buf) ((struct disk_dqblk *)(((char *)buf)+sizeof(struct disk_dqdbheader)))
+
+static inline void mark_quotafile_info_dirty(struct mem_dqinfo *info)
+{
+  info->dqi_flags |= DQF_DIRTY;
+}
+
+static inline int quotafile_info_dirty(struct mem_dqinfo *info)
+{
+  return info->dqi_flags & DQF_DIRTY;
+}
+
+/* Read information header from quota file */
+static int read_quotafile_info(struct super_block *sb, short type)
+{
+	mm_segment_t fs;
+	struct disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct file *f = sb_dqopt(sb)->files[type];
+	ssize_t size;
+	loff_t offset = DQINFOOFF;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dinfo, sizeof(struct disk_dqinfo), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct disk_dqinfo)) {
+		printk(KERN_WARNING "Can't read info structure on device %s.\n",
+			kdevname(f->f_dentry->d_sb->s_dev));
+		return -1;
+	}
+	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
+	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
+	info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
+	info->dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+	info->dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+	info->dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	return 0;
+}
+
+/* Write information header to quota file */
+static int write_quotafile_info(struct super_block *sb, short type)
+{
+	mm_segment_t fs;
+	struct disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct file *f = sb_dqopt(sb)->files[type];
+	ssize_t size;
+	loff_t offset = DQINFOOFF;
+
+	info[type].dqi_flags &= ~DQF_DIRTY;
+	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
+	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
+	dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
+	dinfo.dqi_blocks = cpu_to_le32(info->dqi_blocks);
+	dinfo.dqi_free_blk = cpu_to_le32(info->dqi_free_blk);
+	dinfo.dqi_free_entry = cpu_to_le32(info->dqi_free_entry);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->write(f, (char *)&dinfo, sizeof(struct disk_dqinfo), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct disk_dqinfo)) {
+		printk(KERN_WARNING "Can't write info structure on device %s.\n",
+			kdevname(f->f_dentry->d_sb->s_dev));
+		return -1;
+	}
+	return 0;
+}
+
+static void disk2memdqb(struct mem_dqblk *m, struct disk_dqblk *d)
+{
+	m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
+	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
+	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
+	m->dqb_itime = le64_to_cpu(d->dqb_itime);
+	m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
+	m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
+	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+	m->dqb_btime = le64_to_cpu(d->dqb_btime);
+}
+
+static void mem2diskdqb(struct disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+{
+	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
+	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
+	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
+	d->dqb_itime = cpu_to_le64(m->dqb_itime);
+	d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
+	d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
+	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
+	d->dqb_btime = cpu_to_le64(m->dqb_btime);
+	d->dqb_id = cpu_to_le32(id);
+}
+
+static dqbuf_t getdqbuf(void)
+{
+	dqbuf_t buf = kmalloc(DQBLKSIZE, GFP_KERNEL);
+	if (!buf)
+		printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
+	return buf;
+}
+
+static inline void freedqbuf(dqbuf_t buf)
+{
+	kfree(buf);
+}
+
+static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+	mm_segment_t fs;
+	ssize_t ret;
+	loff_t offset = blk<<DQBLKSIZE_BITS;
+
+	memset(buf, 0, DQBLKSIZE);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->read(filp, (char *)buf, DQBLKSIZE, &offset);
+	set_fs(fs);
+	return ret;
+}
+
+static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+	mm_segment_t fs;
+	ssize_t ret;
+	loff_t offset = blk<<DQBLKSIZE_BITS;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->write(filp, (char *)buf, DQBLKSIZE, &offset);
+	set_fs(fs);
+	return ret;
+
+}
+
+/* Remove empty block from list and return it */
+static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info)
+{
+	dqbuf_t buf = getdqbuf();
+	struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf;
+	int ret, blk;
+
+	if (!buf)
+		return -ENOMEM;
+	if (info->dqi_free_blk) {
+		blk = info->dqi_free_blk;
+		if ((ret = read_blk(filp, blk, buf)) < 0)
+			goto out_buf;
+		info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+	}
+	else {
+		memset(buf, 0, DQBLKSIZE);
+		if ((ret = write_blk(filp, info->dqi_blocks, buf)) < 0)	/* Assure block allocation... */
+			goto out_buf;
+		blk = info->dqi_blocks++;
+	}
+	mark_quotafile_info_dirty(info);
+	ret = blk;
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Insert empty block to the list */
+static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf;
+	int err;
+
+	dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	dh->dqdh_entries = cpu_to_le16(0);
+	info->dqi_free_blk = blk;
+	mark_quotafile_info_dirty(info);
+	if ((err = write_blk(filp, blk, buf)) < 0)	/* Some strange block. We had better leave it... */
+		return err;
+	return 0;
+}
+
+/* Remove given block from the list of blocks with free entries */
+static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf();
+	struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf;
+	uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free);
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	if (nextblk) {
+		if ((err = read_blk(filp, nextblk, tmpbuf)) < 0)
+			goto out_buf;
+		((struct disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
+		if ((err = write_blk(filp, nextblk, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	if (prevblk) {
+		if ((err = read_blk(filp, prevblk, tmpbuf)) < 0)
+			goto out_buf;
+		((struct disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
+		if ((err = write_blk(filp, prevblk, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	else {
+		info->dqi_free_entry = nextblk;
+		mark_quotafile_info_dirty(info);
+	}
+	freedqbuf(tmpbuf);
+	dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+	if (write_blk(filp, blk, buf) < 0)	/* No matter whether write succeeds block is out of list */
+		printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Insert given block to the beginning of list with free entries */
+static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf();
+	struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf;
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	if ((err = write_blk(filp, blk, buf)) < 0)
+		goto out_buf;
+	if (info->dqi_free_entry) {
+		if ((err = read_blk(filp, info->dqi_free_entry, tmpbuf)) < 0)
+			goto out_buf;
+		((struct disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
+		if ((err = write_blk(filp, info->dqi_free_entry, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	freedqbuf(tmpbuf);
+	info->dqi_free_entry = blk;
+	mark_quotafile_info_dirty(info);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Find space for dquot */
+static uint find_free_dqentry(struct dquot *dquot, int *err)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type;
+	uint blk, i;
+	struct disk_dqdbheader *dh;
+	struct disk_dqblk *ddquot;
+	struct disk_dqblk fakedquot;
+	dqbuf_t buf;
+
+	*err = 0;
+	if (!(buf = getdqbuf())) {
+		*err = -ENOMEM;
+		return 0;
+	}
+	dh = (struct disk_dqdbheader *)buf;
+	ddquot = GETENTRIES(buf);
+	if (info->dqi_free_entry) {
+		blk = info->dqi_free_entry;
+		if ((*err = read_blk(filp, blk, buf)) < 0)
+			goto out_buf;
+	}
+	else {
+		blk = get_free_dqblk(filp, info);
+		if ((int)blk < 0) {
+			*err = blk;
+			return 0;
+		}
+		memset(buf, 0, DQBLKSIZE);
+		info->dqi_free_entry = blk;	/* This is enough as block is already zeroed and entry list is empty... */
+		mark_quotafile_info_dirty(info);
+	}
+	if (le16_to_cpu(dh->dqdh_entries)+1 >= DQSTRINBLK)	/* Block will be full? */
+		if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) {
+			printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
+			goto out_buf;
+		}
+	dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1);
+	memset(&fakedquot, 0, sizeof(struct disk_dqblk));
+	/* Find free structure in block */
+	for (i = 0; i < DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct disk_dqblk)); i++);
+#ifdef __DQUOT_PARANOIA
+	if (i == DQSTRINBLK) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
+		*err = -EIO;
+		goto out_buf;
+	}
+#endif
+	if ((*err = write_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk);
+		goto out_buf;
+	}
+	dquot->dq_off = (blk<<DQBLKSIZE_BITS)+sizeof(struct disk_dqdbheader)+i*sizeof(struct disk_dqblk);
+	freedqbuf(buf);
+	return blk;
+out_buf:
+	freedqbuf(buf);
+	return 0;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	dqbuf_t buf;
+	int ret = 0, newson = 0, newact = 0;
+	u32 *ref;
+	uint newblk;
+
+	if (!(buf = getdqbuf()))
+		return -ENOMEM;
+	if (!*treeblk) {
+		ret = get_free_dqblk(filp, info);
+		if (ret < 0)
+			goto out_buf;
+		*treeblk = ret;
+		memset(buf, 0, DQBLKSIZE);
+		newact = 1;
+	}
+	else {
+		if ((ret = read_blk(filp, *treeblk, buf)) < 0) {
+			printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk);
+			goto out_buf;
+		}
+	}
+	ref = (u32 *)buf;
+	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (!newblk)
+		newson = 1;
+	if (depth == DQTREEDEPTH-1) {
+#ifdef __DQUOT_PARANOIA
+		if (newblk) {
+			printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]);
+			ret = -EIO;
+			goto out_buf;
+		}
+#endif
+		newblk = find_free_dqentry(dquot, &ret);
+	}
+	else
+		ret = do_insert_tree(dquot, &newblk, depth+1);
+	if (newson && ret >= 0) {
+		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
+		ret = write_blk(filp, *treeblk, buf);
+	}
+	else if (newact && ret < 0)
+		put_free_dqblk(filp, info, buf, *treeblk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static inline int dq_insert_tree(struct dquot *dquot)
+{
+	int tmp = DQTREEOFF;
+	return do_insert_tree(dquot, &tmp, 0);
+}
+
+/*
  *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
  */
 static void write_dquot(struct dquot *dquot)
@@ -258,32 +639,199 @@
 	mm_segment_t fs;
 	loff_t offset;
 	ssize_t ret;
-	struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem;
-	struct dqblk dqbuf;
+	struct semaphore *sem = &sb_dqopt(dquot->dq_sb)->dqio_sem;
+	struct disk_dqblk ddquot;
 
 	down(sem);
-	filp = dquot->dq_sb->s_dquot.files[type];
-	offset = dqoff(dquot->dq_id);
+	if (!dquot->dq_off)
+		if ((ret = dq_insert_tree(dquot)) < 0) {
+			printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret);
+			goto out_sem;
+		}
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+	offset = dquot->dq_off;
+	mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
 	fs = get_fs();
 	set_fs(KERNEL_DS);
-
-	/*
-	 * Note: clear the DQ_MOD flag unconditionally,
-	 * so we don't loop forever on failure.
-	 */
-	memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk));
-	dquot->dq_flags &= ~DQ_MOD;
-	ret = 0;
-	if (filp)
-		ret = filp->f_op->write(filp, (char *)&dqbuf, 
-					sizeof(struct dqblk), &offset);
-	if (ret != sizeof(struct dqblk))
+	ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct disk_dqblk), &offset);
+	set_fs(fs);
+	if (ret != sizeof(struct disk_dqblk))
 		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
 			kdevname(dquot->dq_dev));
-
-	set_fs(fs);
-	up(sem);
 	dqstats.writes++;
+out_sem:
+	up(sem);
+}
+
+/* Free dquot entry in data block */
+static int free_dqentry(struct dquot *dquot, uint blk)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	struct disk_dqdbheader *dh;
+	dqbuf_t buf = getdqbuf();
+	int ret = 0;
+
+	if (!buf)
+		return -ENOMEM;
+	if (dquot->dq_off >> DQBLKSIZE_BITS != blk) {
+		printk(KERN_ERR "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", blk, (uint)(dquot->dq_off >> DQBLKSIZE_BITS));
+		goto out_buf;
+	}
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+		goto out_buf;
+	}
+	dh = (struct disk_dqdbheader *)buf;
+	dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1);
+	if (!le16_to_cpu(dh->dqdh_entries)) {	/* Block got free? */
+		if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 ||
+		    (ret = put_free_dqblk(filp, info, buf, blk)) < 0) {
+			printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk);
+			goto out_buf;
+		}
+	}
+	else {
+		memset(buf+(dquot->dq_off & ((1 << DQBLKSIZE_BITS)-1)), 0, sizeof(struct disk_dqblk));
+		if (le16_to_cpu(dh->dqdh_entries) == DQSTRINBLK-1) {
+			/* Insert will write block itself */
+			if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) {
+				printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk);
+				goto out_buf;
+			}
+		}
+		else
+			if ((ret = write_blk(filp, blk, buf)) < 0) {
+				printk(KERN_ERR "VFS: Can't write quota data block %u\n", blk);
+				goto out_buf;
+			}
+	}
+	dquot->dq_off = 0;	/* Quota is now unattached */
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Remove reference to dquot from tree */
+static int remove_tree(struct dquot *dquot, uint *blk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	dqbuf_t buf = getdqbuf();
+	int ret = 0;
+	uint newblk;
+	u32 *ref = (u32 *)buf;
+	
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, *blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+		goto out_buf;
+	}
+	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (depth == DQTREEDEPTH-1) {
+		ret = free_dqentry(dquot, newblk);
+		newblk = 0;
+	}
+	else
+		ret = remove_tree(dquot, &newblk, depth+1);
+	if (ret >= 0 && !newblk) {
+		int i;
+		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
+		for (i = 0; i < DQBLKSIZE && !buf[i]; i++);	/* Block got empty? */
+		if (i == DQBLKSIZE) {
+			put_free_dqblk(filp, info, buf, *blk);
+			*blk = 0;
+		}
+		else
+			if ((ret = write_blk(filp, *blk, buf)) < 0)
+				printk(KERN_ERR "VFS: Can't write quota tree block %u.\n", *blk);
+	}
+out_buf:
+	freedqbuf(buf);
+	return ret;	
+}
+
+/* Delete dquot from tree */
+static void delete_dquot(struct dquot *dquot)
+{
+	uint tmp = DQTREEOFF;
+
+	if (!dquot->dq_off)	/* Even not allocated? */
+		return;
+	down(&sb_dqopt(dquot->dq_sb)->dqio_sem);
+	remove_tree(dquot, &tmp, 0);
+	up(&sb_dqopt(dquot->dq_sb)->dqio_sem);
+}
+
+/* Find entry in block */
+static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	dqbuf_t buf = getdqbuf();
+	loff_t ret = 0;
+	int i;
+	struct disk_dqblk *ddquot = GETENTRIES(buf);
+
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	if (dquot->dq_id)
+		for (i = 0; i < DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
+	else {	/* ID 0 as a bit more complicated searching... */
+		struct disk_dqblk fakedquot;
+
+		memset(&fakedquot, 0, sizeof(struct disk_dqblk));
+		for (i = 0; i < DQSTRINBLK; i++)
+			if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct disk_dqblk)))
+				break;
+	}
+	if (i == DQSTRINBLK) {
+		printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id);
+		ret = -EIO;
+		goto out_buf;
+	}
+	else
+		ret = (blk << DQBLKSIZE_BITS) + sizeof(struct disk_dqdbheader) + i * sizeof(struct disk_dqblk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree */
+static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	dqbuf_t buf = getdqbuf();
+	loff_t ret = 0;
+	u32 *ref = (u32 *)buf;
+
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	ret = 0;
+	blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (!blk)	/* No reference? */
+		goto out_buf;
+	if (depth < DQTREEDEPTH-1)
+		ret = find_tree_dqentry(dquot, blk, depth+1);
+	else
+		ret = find_block_dqentry(dquot, blk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline loff_t find_dqentry(struct dquot *dquot)
+{
+	return find_tree_dqentry(dquot, DQTREEOFF, 0);
 }
 
 static void read_dquot(struct dquot *dquot)
@@ -292,31 +840,61 @@
 	struct file *filp;
 	mm_segment_t fs;
 	loff_t offset;
+	struct disk_dqblk ddquot;
 
-	filp = dquot->dq_sb->s_dquot.files[type];
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
 	if (filp == (struct file *)NULL)
 		return;
 
 	lock_dquot(dquot);
-	if (!dquot->dq_sb)	/* Invalidated quota? */
+#ifdef __DQUOT_PARANOIA
+	if (!dquot->dq_sb) {	/* Invalidated quota? */
+		printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
 		goto out_lock;
+	}
+#endif
 	/* Now we are sure filp is valid - the dquot isn't invalidated */
-	down(&dquot->dq_sb->s_dquot.dqio_sem);
-	offset = dqoff(dquot->dq_id);
-	fs = get_fs();
-	set_fs(KERNEL_DS);
-	filp->f_op->read(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset);
-	up(&dquot->dq_sb->s_dquot.dqio_sem);
-	set_fs(fs);
-
-	if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
-	    dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)
+	down(&sb_dqopt(dquot->dq_sb)->dqio_sem);
+	offset = find_dqentry(dquot);
+	if (offset <= 0) {	/* Entry not present? */
+		if (offset < 0)
+			printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id);
+		dquot->dq_off = 0;
 		dquot->dq_flags |= DQ_FAKE;
+		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+	}
+	else {
+		dquot->dq_off = offset;
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		filp->f_op->read(filp, (char *)&ddquot, sizeof(struct disk_dqblk), &offset);
+		set_fs(fs);
+		disk2memdqb(&dquot->dq_dqb, &ddquot);
+	}
+	up(&sb_dqopt(dquot->dq_sb)->dqio_sem);
 	dqstats.reads++;
 out_lock:
 	unlock_dquot(dquot);
 }
 
+/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */
+static void commit_dquot(struct dquot *dquot)
+{
+	lock_dquot(dquot);
+	/* We clear the flag everytime so we don't loop when there was an IO error... */
+	dquot->dq_flags &= ~DQ_MOD;
+	if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
+		delete_dquot(dquot);
+	else
+		write_dquot(dquot);
+	unlock_dquot(dquot);
+}
+
+/*
+ *	End of IO functions
+ */
+
+
 /*
  * Unhash and selectively clear the dquot structure,
  * but preserve the use count, list pointers, and
@@ -331,8 +909,9 @@
 	dquot->dq_dev = NODEV;
 	dquot->dq_type = -1;
 	dquot->dq_flags = 0;
+	dquot->dq_off = 0;
 	dquot->dq_referenced = 0;
-	memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
+	memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
 }
 
 /* Invalidate all dquots on the list, wait for all users. Note that this function is called
@@ -389,7 +968,7 @@
 		if (dquot->dq_flags & DQ_LOCKED)
 			wait_on_dquot(dquot);
 		if (dquot->dq_flags & DQ_MOD)
-			write_dquot(dquot);
+			commit_dquot(dquot);
 		dqput(dquot);
 		goto restart;
 	}
@@ -442,16 +1021,17 @@
 		return;
 	}
 	if (dquot->dq_flags & DQ_MOD) {
-		write_dquot(dquot);
+		commit_dquot(dquot);
 		goto we_slept;
 	}
 
-	/* sanity check */
+#ifdef __DQUOT_PARANOIA
 	if (!list_empty(&dquot->dq_free)) {
 		printk(KERN_ERR "dqput: dquot already on free list??\n");
 		dquot->dq_count--;	/* J.K. Just decrementing use count seems safer... */
 		return;
 	}
+#endif
 	dquot->dq_count--;
 	/* Place at end of LRU free queue */
 	put_dquot_last(dquot);
@@ -483,7 +1063,7 @@
 {
 	unsigned int hashent = hashfn(sb->s_dev, id, type);
 	struct dquot *dquot, *empty = NODQUOT;
-	struct quota_mount_options *dqopt = sb_dqopt(sb);
+	struct quota_info *dqopt = sb_dqopt(sb);
 
 we_slept:
         if (!is_enabled(dqopt, type)) {
@@ -515,11 +1095,13 @@
 			dqput(empty);
 	}
 
+#ifdef __DQUOT_PARANOIA
 	if (!dquot->dq_sb) {	/* Has somebody invalidated entry under us? */
 		printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n");
 		dqput(dquot);
 		return NODQUOT;
 	}
+#endif
 	dquot->dq_referenced++;
 	dqstats.lookups++;
 
@@ -531,6 +1113,7 @@
 	if (dquot == NODQUOT)
 		return NODQUOT;
 	dquot->dq_count++;
+#ifdef __DQUOT_PARANOIA
 	if (!dquot->dq_sb) {
 		printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
 		dquot->dq_count--;
@@ -538,6 +1121,7 @@
 	}
 	if (dquot->dq_flags & DQ_LOCKED)
 		printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
+#endif
 	dquot->dq_referenced++;
 	dqstats.lookups++;
 	return dquot;
@@ -547,7 +1131,7 @@
 static inline int is_quotafile(struct inode *inode)
 {
 	int cnt;
-	struct quota_mount_options *dqopt = sb_dqopt(inode->i_sb);
+	struct quota_info *dqopt = sb_dqopt(inode->i_sb);
 	struct file **files;
 
 	if (!dqopt)
@@ -658,9 +1242,9 @@
 	dquot->dq_flags |= DQ_MOD;
 }
 
-static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number)
+static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
 {
-	dquot->dq_curblocks += number;
+	dquot->dq_curspace += number;
 	dquot->dq_flags |= DQ_MOD;
 }
 
@@ -676,13 +1260,13 @@
 	dquot->dq_flags |= DQ_MOD;
 }
 
-static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number)
+static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
 {
-	if (dquot->dq_curblocks > number)
-		dquot->dq_curblocks -= number;
+	if (dquot->dq_curspace > number)
+		dquot->dq_curspace -= number;
 	else
-		dquot->dq_curblocks = 0;
-	if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+		dquot->dq_curspace = 0;
+	if (toqb(dquot->dq_curspace) < dquot->dq_bsoftlimit)
 		dquot->dq_btime = (time_t) 0;
 	dquot->dq_flags &= ~DQ_BLKS;
 	dquot->dq_flags |= DQ_MOD;
@@ -723,7 +1307,7 @@
 		tty_write_message(current->tty, ": warning, ");
 	else
 		tty_write_message(current->tty, ": write failed, ");
-	tty_write_message(current->tty, quotatypes[dquot->dq_type]);
+	tty_write_message(current->tty, (char *)quotatypes[dquot->dq_type]);
 	switch (warntype) {
 		case IHARDWARN:
 			msg = " file limit reached.\n";
@@ -758,7 +1342,7 @@
 
 static inline char ignore_hardlimit(struct dquot *dquot)
 {
-	return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type];
+	return capable(CAP_SYS_RESOURCE);
 }
 
 static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
@@ -786,20 +1370,20 @@
 	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
 	    dquot->dq_itime == 0) {
 		*warntype = ISOFTWARN;
-		dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type];
+		dquot->dq_itime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace);
 	}
 
 	return QUOTA_OK;
 }
 
-static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *warntype)
+static int check_bdq(struct dquot *dquot, qsize_t space, char prealloc, char *warntype)
 {
 	*warntype = 0;
-	if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
+	if (space <= 0 || dquot->dq_flags & DQ_FAKE)
 		return QUOTA_OK;
 
 	if (dquot->dq_bhardlimit &&
-	   (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit &&
+	    toqb(dquot->dq_curspace + space) > dquot->dq_bhardlimit &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
 			*warntype = BHARDWARN;
@@ -807,7 +1391,7 @@
 	}
 
 	if (dquot->dq_bsoftlimit &&
-	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+	    toqb(dquot->dq_curspace + space) > dquot->dq_bsoftlimit &&
 	    dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
@@ -816,11 +1400,11 @@
 	}
 
 	if (dquot->dq_bsoftlimit &&
-	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+	    toqb(dquot->dq_curspace + space) > dquot->dq_bsoftlimit &&
 	    dquot->dq_btime == 0) {
 		if (!prealloc) {
 			*warntype = BSOFTWARN;
-			dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type];
+			dquot->dq_btime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace);
 		}
 		else
 			/*
@@ -837,18 +1421,18 @@
  * Initialize a dquot-struct with new quota info. This is used by the
  * system call interface functions.
  */ 
-static int set_dqblk(struct super_block *sb, int id, short type, int flags, struct dqblk *dqblk)
+static int set_dqblk(struct super_block *sb, qid_t id, short type, int flags, struct mem_dqblk *dqblk)
 {
 	struct dquot *dquot;
 	int error = -EFAULT;
-	struct dqblk dq_dqblk;
+	struct mem_dqblk dq_dqblk;
 
-	if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
+	if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct mem_dqblk)))
 		return error;
 
 	if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) {
 		/* We can't block while changing quota structure... */
-		if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
+		if ((flags & SET_QUOTA) || (flags & SET_QLIMIT)) {
 			dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
 			dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
 			dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
@@ -859,22 +1443,22 @@
 			if (dquot->dq_isoftlimit &&
 			    dquot->dq_curinodes < dquot->dq_isoftlimit &&
 			    dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
-				dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[type];
+				dquot->dq_itime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace);
 			dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
-			if (dquot->dq_curinodes < dquot->dq_isoftlimit)
-				dquot->dq_flags &= ~DQ_INODES;
 			if (dquot->dq_bsoftlimit &&
-			    dquot->dq_curblocks < dquot->dq_bsoftlimit &&
-			    dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
-				dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[type];
-			dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
-			if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
-				dquot->dq_flags &= ~DQ_BLKS;
+			    toqb(dquot->dq_curspace) < dquot->dq_bsoftlimit &&
+			    toqb(dq_dqblk.dqb_curspace) >= dquot->dq_bsoftlimit)
+				dquot->dq_btime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace);
+			dquot->dq_curspace = dq_dqblk.dqb_curspace;
 		}
 
-		if (id == 0) {
-			dquot->dq_sb->s_dquot.block_expire[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
-			dquot->dq_sb->s_dquot.inode_expire[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
+		if (dquot->dq_curinodes < dquot->dq_isoftlimit || !dquot->dq_isoftlimit) {
+			dquot->dq_itime = 0;
+			dquot->dq_flags &= ~DQ_INODES;
+		}
+		if (toqb(dquot->dq_curspace) < dquot->dq_bsoftlimit || !dquot->dq_bsoftlimit) {
+			dquot->dq_btime = 0;
+			dquot->dq_flags &= ~DQ_BLKS;
 		}
 
 		if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
@@ -889,10 +1473,10 @@
 	return 0;
 }
 
-static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk)
+static int get_quota(struct super_block *sb, qid_t id, short type, struct mem_dqblk *dqblk)
 {
 	struct dquot *dquot;
-	struct dqblk data;
+	struct mem_dqblk data;
 	int error = -ESRCH;
 
 	if (!sb || !sb_has_quota_enabled(sb, type))
@@ -901,10 +1485,10 @@
 	if (dquot == NODQUOT)
 		goto out;
 
-	memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk));        /* We copy data to preserve them from changing */
+	memcpy(&data, &dquot->dq_dqb, sizeof(struct mem_dqblk));        /* We copy data to preserve them from changing */
 	dqput(dquot);
 	error = -EFAULT;
-	if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct dqblk)))
+	if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct mem_dqblk)))
 		error = 0;
 out:
 	return error;
@@ -925,46 +1509,48 @@
 	return error;
 }
 
-static int quota_root_squash(struct super_block *sb, short type, int *addr)
+static int get_info(struct super_block *sb, short type, struct mem_dqinfo *pinfo)
 {
-	int new_value, error;
-
-	if (!sb)
-		return(-ENODEV);
-
-	error = -EFAULT;
-	if (!copy_from_user(&new_value, addr, sizeof(int))) {
-		sb_dqopt(sb)->rsquash[type] = new_value;
-		error = 0;
-	}
-	return error;
+	struct mem_dqinfo kinfo;
+	
+	if (!sb || !sb_has_quota_enabled(sb, type))
+		return -ESRCH;
+	/* Make our own copy so we can guarantee consistent structure */
+	memcpy(&kinfo, sb_dqopt(sb)->info+type, sizeof(struct mem_dqinfo));
+	kinfo.dqi_flags &= DQF_MASK;
+	if (copy_to_user(pinfo, &kinfo, sizeof(struct mem_dqinfo)))
+		return -EFAULT;
+	return 0;
 }
 
-#if 0	/* We are not going to support filesystems without i_blocks... */
-/*
- * This is a simple algorithm that calculates the size of a file in blocks.
- * This is only used on filesystems that do not have an i_blocks count.
- */
-static u_long isize_to_blocks(loff_t isize, size_t blksize_bits)
+static int set_info(int op, struct super_block *sb, short type, struct mem_dqinfo *pinfo)
 {
-	u_long blocks;
-	u_long indirect;
+	struct mem_dqinfo info;
+	struct quota_info *dqopt = sb_dqopt(sb);
 
-	if (!blksize_bits)
-		blksize_bits = BLOCK_SIZE_BITS;
-	blocks = (isize >> blksize_bits) + ((isize & ~((1 << blksize_bits)-1)) ? 1 : 0);
-	if (blocks > 10) {
-		indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
-		if (blocks > (10 + 256)) {
-			indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
-			if (blocks > (10 + 256 + (256 << 8)))
-				indirect++; /* triple indirect blocks */
-		}
-		blocks += indirect;
-	}
-	return blocks;
+	if (!sb || !sb_has_quota_enabled(sb, type))
+		return -ESRCH;
+
+	if (copy_from_user(&info, pinfo, sizeof(struct mem_dqinfo)))
+		return -EFAULT;
+	
+	switch (op) {
+		case ISET_FLAGS:
+			dqopt->info[type].dqi_flags = (dqopt->info[type].dqi_flags & ~DQF_MASK)
+			  | (info.dqi_flags & DQF_MASK);
+			break;
+		case ISET_GRACE:
+			dqopt->info[type].dqi_bgrace = info.dqi_bgrace;
+			dqopt->info[type].dqi_igrace = info.dqi_igrace;
+			break;
+		case ISET_ALL:
+			info.dqi_flags &= ~DQF_MASK;
+			memcpy(dqopt->info + type, &info, sizeof(struct mem_dqinfo));
+			break;
+ 	}
+	mark_quotafile_info_dirty(dqopt->info + type);
+	return 0;
 }
-#endif
 
 /*
  * Externally referenced functions through dquot_operations in inode.
@@ -974,7 +1560,7 @@
 void dquot_initialize(struct inode *inode, short type)
 {
 	struct dquot *dquot[MAXQUOTAS];
-	unsigned int id = 0;
+	qid_t id = 0;
 	short cnt;
 
 	if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) &&
@@ -1042,7 +1628,7 @@
 /*
  * This operation can block, but only after everything is updated
  */
-int dquot_alloc_block(struct inode *inode, unsigned long number, char warn)
+int dquot_alloc_space(struct inode *inode, qsize_t number, char prealloc)
 {
 	int cnt, ret = NO_QUOTA;
 	struct dquot *dquot[MAXQUOTAS];
@@ -1056,15 +1642,15 @@
 		dquot[cnt] = dqduplicate(inode->i_dquot[cnt]);
 		if (dquot[cnt] == NODQUOT)
 			continue;
-		if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA)
+		if (check_bdq(dquot[cnt], number, prealloc, warntype+cnt) == NO_QUOTA)
 			goto warn_put_all;
 	}
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (dquot[cnt] == NODQUOT)
 			continue;
-		dquot_incr_blocks(dquot[cnt], number);
+		dquot_incr_space(dquot[cnt], number);
 	}
-	inode->i_blocks += number << (BLOCK_SIZE_BITS - 9);
+	inode_add_bytes(inode, number);
 	mark_inode_dirty(inode);
 	ret = QUOTA_OK;
 warn_put_all:
@@ -1113,7 +1699,7 @@
 /*
  * This is a non-blocking operation.
  */
-void dquot_free_block(struct inode *inode, unsigned long number)
+void dquot_free_space(struct inode *inode, qsize_t number)
 {
 	unsigned short cnt;
 	struct dquot *dquot;
@@ -1122,10 +1708,10 @@
 		dquot = dqduplicate(inode->i_dquot[cnt]);
 		if (dquot == NODQUOT)
 			continue;
-		dquot_decr_blocks(dquot, number);
+		dquot_decr_space(dquot, number);
 		dqput(dquot);
 	}
-	inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9);
+	inode_sub_bytes(inode, number);
 	mark_inode_dirty(inode);
 }
 
@@ -1153,7 +1739,7 @@
  */
 int dquot_transfer(struct inode *inode, struct iattr *iattr)
 {
-	unsigned long blocks;
+	qsize_t space;
 	struct dquot *transfer_from[MAXQUOTAS];
 	struct dquot *transfer_to[MAXQUOTAS];
 	int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
@@ -1183,7 +1769,7 @@
 		}
 	}
 	/* NOBLOCK START: From now on we shouldn't block */
-	blocks = (inode->i_blocks >> 1);
+	space = inode_get_bytes(inode);
 	/* Build the transfer_from list and check the limits */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		/* The second test can fail when quotaoff is in progress... */
@@ -1193,7 +1779,7 @@
 		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)
+		    check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
 			goto warn_put_all;
 	}
 
@@ -1208,10 +1794,10 @@
 			continue;
 
 		dquot_decr_inodes(transfer_from[cnt], 1);
-		dquot_decr_blocks(transfer_from[cnt], blocks);
+		dquot_decr_space(transfer_from[cnt], space);
 
 		dquot_incr_inodes(transfer_to[cnt], 1);
-		dquot_incr_blocks(transfer_to[cnt], blocks);
+		dquot_incr_space(transfer_to[cnt], space);
 
 		if (inode->i_dquot[cnt] == NODQUOT)
 			BUG();
@@ -1251,14 +1837,14 @@
 struct dquot_operations dquot_operations = {
 	dquot_initialize,		/* mandatory */
 	dquot_drop,			/* mandatory */
-	dquot_alloc_block,
+	dquot_alloc_space,
 	dquot_alloc_inode,
-	dquot_free_block,
+	dquot_free_space,
 	dquot_free_inode,
 	dquot_transfer
 };
 
-static inline void set_enable_flags(struct quota_mount_options *dqopt, short type)
+static inline void set_enable_flags(struct quota_info *dqopt, short type)
 {
 	switch (type) {
 		case USRQUOTA:
@@ -1270,7 +1856,7 @@
 	}
 }
 
-static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type)
+static inline void reset_enable_flags(struct quota_info *dqopt, short type)
 {
 	switch (type) {
 		case USRQUOTA:
@@ -1292,7 +1878,7 @@
 {
 	struct file *filp;
 	short cnt;
-	struct quota_mount_options *dqopt = sb_dqopt(sb);
+	struct quota_info *dqopt = sb_dqopt(sb);
 
 	if (!sb)
 		goto out;
@@ -1309,11 +1895,12 @@
 		/* Note: these are blocking operations */
 		remove_dquot_ref(sb->s_dev, cnt);
 		invalidate_dquots(sb->s_dev, cnt);
-
+		/* When invalidate is finished there are no users of any dquot of our interest... */
+		if (quotafile_info_dirty(sb_dqopt(sb)->info+cnt))
+			write_quotafile_info(sb, cnt);
 		filp = dqopt->files[cnt];
 		dqopt->files[cnt] = (struct file *)NULL;
-		dqopt->inode_expire[cnt] = 0;
-		dqopt->block_expire[cnt] = 0;
+		memset(dqopt->info+cnt, 0, sizeof(struct mem_dqinfo));
 		fput(filp);
 	}	
 	up(&dqopt->dqoff_sem);
@@ -1321,20 +1908,31 @@
 	return 0;
 }
 
-static inline int check_quotafile_size(loff_t size)
+static int check_quotafile(struct file *f, short type)
 {
-	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));
+	struct disk_dqheader dqhead;
+	mm_segment_t fs;
+	ssize_t size;
+	loff_t offset = 0;
+ 
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dqhead, sizeof(struct disk_dqheader), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct disk_dqheader))
+		return 0;
+	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
+	    le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
+		return 0;
+	return 1;
 }
 
+/* Turn quotas of given type on */
 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);
+	struct quota_info *dqopt = sb_dqopt(sb);
 	char *tmp;
 	int error;
 
@@ -1361,24 +1959,21 @@
 	if (!S_ISREG(inode->i_mode))
 		goto out_f;
 	error = -EINVAL;
-	if (inode->i_size == 0 || !check_quotafile_size(inode->i_size))
+	if (inode->i_size == 0 || !check_quotafile(f, type))
 		goto out_f;
 	dquot_drop(inode);	/* We don't want quota on quota files - open might initialize the other quota type... */
 
 	dqopt->files[type] = f;
-	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);
-
+	if (read_quotafile_info(sb, type) < 0)	/* Read header from file - OK? */
+		goto out_f_tab;
 	sb->dq_op = &dquot_operations;
+	set_enable_flags(dqopt, type);
 	add_dquot_ref(sb, type);
 
 	up(&dqopt->dqoff_sem);
 	return 0;
-
+out_f_tab:
+	dqopt->files[type] = NULL;
 out_f:
 	filp_close(f, NULL);
 out_lock:
@@ -1393,7 +1988,7 @@
  * calls. Maybe we need to add the process quotas etc. in the future,
  * but we probably should use rlimits for that.
  */
-asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+asmlinkage long sys_quotactl(int cmd, const char *special, qid_t id, __kernel_caddr_t addr)
 {
 	int cmds = 0, type = 0, flags = 0;
 	kdev_t dev;
@@ -1404,9 +1999,9 @@
 	cmds = cmd >> SUBCMDSHIFT;
 	type = cmd & SUBCMDMASK;
 
-	if ((u_int) type >= MAXQUOTAS)
-		goto out;
-	if (id & ~0xFFFF)
+
+	if ((uint) type >= MAXQUOTAS || cmds > 0x0F00 || cmds < 0x100 || cmds == 0x0300 ||
+	    cmds == 0x0400 || cmds == 0x0500)
 		goto out;
 
 	ret = -EPERM;
@@ -1425,7 +2020,6 @@
 				goto out;
 	}
 
-	ret = -EINVAL;
 	dev = NODEV;
 	if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
 		mode_t mode;
@@ -1457,7 +2051,7 @@
 			ret = quota_off(sb, type);
 			goto out;
 		case Q_GETQUOTA:
-			ret = get_quota(sb, id, type, (struct dqblk *) addr);
+			ret = get_quota(sb, id, type, (struct mem_dqblk *) addr);
 			goto out;
 		case Q_SETQUOTA:
 			flags |= SET_QUOTA;
@@ -1474,16 +2068,25 @@
 		case Q_GETSTATS:
 			ret = get_stats(addr);
 			goto out;
-		case Q_RSQUASH:
-			ret = quota_root_squash(sb, type, (int *) addr);
+		case Q_GETINFO:
+			ret = get_info(sb, type, (struct mem_dqinfo *) addr);
+			goto out;
+		case Q_SETFLAGS:
+			ret = set_info(ISET_FLAGS, sb, type, (struct mem_dqinfo *)addr);
+			goto out;
+		case Q_SETGRACE:
+			ret = set_info(ISET_GRACE, sb, type, (struct mem_dqinfo *)addr);
 			goto out;
+		case Q_SETINFO:
+			ret = set_info(ISET_ALL, sb, type, (struct mem_dqinfo *)addr);
+ 			goto out;
 		default:
 			goto out;
 	}
 
 	ret = -NODEV;
 	if (sb && sb_has_quota_enabled(sb, type))
-		ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr);
+		ret = set_dqblk(sb, id, type, flags, (struct mem_dqblk *) addr);
 out:
 	unlock_kernel();
 	return ret;
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/fs/inode.c linux-2.4.0-test11-newquota/fs/inode.c
--- linux-2.4.0-test11-quotafix/fs/inode.c	Fri Nov  3 22:07:22 2000
+++ linux-2.4.0-test11-newquota/fs/inode.c	Mon Nov 27 10:08:00 2000
@@ -523,6 +523,8 @@
 	inode->i_nlink = 1;
 	atomic_set(&inode->i_writecount, 0);
 	inode->i_size = 0;
+	inode->i_blocks = 0;
+	inode->i_bytes = 0;
 	inode->i_generation = 0;
 	memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
 	inode->i_pipe = NULL;
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/fs/ioctl.c linux-2.4.0-test11-newquota/fs/ioctl.c
--- linux-2.4.0-test11-quotafix/fs/ioctl.c	Fri Oct  6 00:10:57 2000
+++ linux-2.4.0-test11-newquota/fs/ioctl.c	Sun Dec  3 21:11:41 2000
@@ -100,6 +100,15 @@
 				filp->f_flags &= ~FASYNC;
 			break;
 
+		case FIOQSIZE:
+			if (S_ISDIR(filp->f_dentry->d_inode->i_mode) ||
+			    S_ISREG(filp->f_dentry->d_inode->i_mode) ||
+			    S_ISLNK(filp->f_dentry->d_inode->i_mode))
+				*(loff_t *)arg = inode_get_bytes(filp->f_dentry->d_inode);
+			else
+				error = -ENOTTY;
+			break;
+
 		default:
 			error = -ENOTTY;
 			if (S_ISREG(filp->f_dentry->d_inode->i_mode))
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-alpha/ioctls.h linux-2.4.0-test11-newquota/include/asm-alpha/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-alpha/ioctls.h	Fri Oct  6 00:11:06 2000
+++ linux-2.4.0-test11-newquota/include/asm-alpha/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -9,6 +9,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TIOCGETP	_IOR('t', 8, struct sgttyb)
 #define TIOCSETP	_IOW('t', 9, struct sgttyb)
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-arm/ioctls.h linux-2.4.0-test11-newquota/include/asm-arm/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-arm/ioctls.h	Fri Oct  6 00:11:09 2000
+++ linux-2.4.0-test11-newquota/include/asm-arm/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -65,6 +65,7 @@
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
+#define FIOQSIZE	0x545E
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-i386/ioctls.h linux-2.4.0-test11-newquota/include/asm-i386/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-i386/ioctls.h	Fri Oct  6 00:11:04 2000
+++ linux-2.4.0-test11-newquota/include/asm-i386/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -67,6 +67,7 @@
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
 #define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+#define FIOQSIZE	0x5460
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-ia64/ioctls.h linux-2.4.0-test11-newquota/include/asm-ia64/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-ia64/ioctls.h	Fri Oct  6 00:11:14 2000
+++ linux-2.4.0-test11-newquota/include/asm-ia64/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -72,6 +72,7 @@
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
 #define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+#define FIOQSIZE	0x5460
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-m68k/ioctls.h linux-2.4.0-test11-newquota/include/asm-m68k/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-m68k/ioctls.h	Fri Oct  6 00:11:08 2000
+++ linux-2.4.0-test11-newquota/include/asm-m68k/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -65,6 +65,7 @@
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
+#define FIOQSIZE	0x545E
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-mips/ioctls.h linux-2.4.0-test11-newquota/include/asm-mips/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-mips/ioctls.h	Fri Oct  6 00:11:05 2000
+++ linux-2.4.0-test11-newquota/include/asm-mips/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -58,6 +58,7 @@
 #define FIONCLEX	0x6602		/* these numbers need to be adjusted. */
 #define FIOASYNC	0x667d
 #define FIONBIO		0x667e
+#define FIOQSIZE	0x667f
 
 #if defined(__USE_MISC) || defined (__KERNEL__)
 #define TIOCGLTC	(tIOC | 116)		/* get special local chars */
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-mips64/ioctls.h linux-2.4.0-test11-newquota/include/asm-mips64/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-mips64/ioctls.h	Fri Oct  6 00:11:15 2000
+++ linux-2.4.0-test11-newquota/include/asm-mips64/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -58,6 +58,7 @@
 #define FIONCLEX	0x6602		/* these numbers need to be adjusted. */
 #define FIOASYNC	0x667d
 #define FIONBIO		0x667e
+#define FIOQSIZE	0x667f
 
 #if defined(__USE_MISC) || defined (__KERNEL__)
 #define TIOCGLTC	(tIOC | 116)		/* get special local chars */
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-ppc/ioctls.h linux-2.4.0-test11-newquota/include/asm-ppc/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-ppc/ioctls.h	Fri Oct  6 00:11:09 2000
+++ linux-2.4.0-test11-newquota/include/asm-ppc/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -9,6 +9,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TIOCGETP	_IOR('t', 8, struct sgttyb)
 #define TIOCSETP	_IOW('t', 9, struct sgttyb)
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-ppc/termios.h linux-2.4.0-test11-newquota/include/asm-ppc/termios.h
--- linux-2.4.0-test11-quotafix/include/asm-ppc/termios.h	Fri Oct  6 00:11:09 2000
+++ linux-2.4.0-test11-newquota/include/asm-ppc/termios.h	Sun Dec  3 21:12:21 2000
@@ -43,6 +43,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TIOCGETP	_IOR('t', 8, struct sgttyb)
 #define TIOCSETP	_IOW('t', 9, struct sgttyb)
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-s390/ioctls.h linux-2.4.0-test11-newquota/include/asm-s390/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-s390/ioctls.h	Fri Oct  6 00:11:16 2000
+++ linux-2.4.0-test11-newquota/include/asm-s390/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -73,6 +73,7 @@
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
+#define FIOQSIZE	0x545E
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-sh/ioctls.h linux-2.4.0-test11-newquota/include/asm-sh/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-sh/ioctls.h	Fri Oct  6 00:11:11 2000
+++ linux-2.4.0-test11-newquota/include/asm-sh/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -9,6 +9,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TCGETS		0x5401
 #define TCSETS		0x5402
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-sparc/ioctls.h linux-2.4.0-test11-newquota/include/asm-sparc/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-sparc/ioctls.h	Fri Oct  6 00:11:08 2000
+++ linux-2.4.0-test11-newquota/include/asm-sparc/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -86,6 +86,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
  * someday.  This is completely bogus, I know...
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-sparc64/ioctls.h linux-2.4.0-test11-newquota/include/asm-sparc64/ioctls.h
--- linux-2.4.0-test11-quotafix/include/asm-sparc64/ioctls.h	Fri Oct  6 00:11:09 2000
+++ linux-2.4.0-test11-newquota/include/asm-sparc64/ioctls.h	Sun Dec  3 21:12:21 2000
@@ -87,6 +87,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
  * someday.  This is completely bogus, I know...
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/linux/fs.h linux-2.4.0-test11-newquota/include/linux/fs.h
--- linux-2.4.0-test11-quotafix/include/linux/fs.h	Mon Nov 27 09:43:43 2000
+++ linux-2.4.0-test11-newquota/include/linux/fs.h	Sun Dec  3 21:13:00 2000
@@ -398,6 +398,7 @@
 	unsigned long		i_blksize;
 	unsigned long		i_blocks;
 	unsigned long		i_version;
+	unsigned short		i_bytes;
 	struct semaphore	i_sem;
 	struct semaphore	i_zombie;
 	struct inode_operations	*i_op;
@@ -464,6 +465,39 @@
 		__mark_inode_dirty(inode);
 }
 
+static inline void inode_add_bytes(struct inode *inode, loff_t bytes)
+{
+	inode->i_blocks += bytes >> 9;
+	bytes &= 511;
+	inode->i_bytes += bytes;
+	if (inode->i_bytes >= 512) {
+		inode->i_blocks++;
+		inode->i_bytes -= 512;
+	}
+}
+
+static inline void inode_sub_bytes(struct inode *inode, loff_t bytes)
+{
+	inode->i_blocks -= bytes >> 9;
+	bytes &= 511;
+	if (inode->i_bytes < bytes) {
+		inode->i_blocks--;
+		inode->i_bytes += 512;
+	}
+	inode->i_bytes -= bytes;
+}
+
+static inline loff_t inode_get_bytes(struct inode *inode)
+{
+	return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
+}
+
+static inline void inode_set_bytes(struct inode *inode, loff_t bytes)
+{
+	inode->i_blocks = bytes >> 9;
+	inode->i_bytes = bytes & 511;
+}
+
 struct fown_struct {
 	int pid;		/* pid or -pgrp where SIGIO should be sent */
 	uid_t uid, euid;	/* uid/euid of process setting the owner */
@@ -596,15 +630,13 @@
 #define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
 #define DQUOT_GRP_ENABLED	0x02		/* Group diskquotas enabled */
 
-struct quota_mount_options
+struct quota_info
 {
 	unsigned int flags;			/* Flags for diskquotas on this device */
 	struct semaphore dqio_sem;		/* lock device while I/O in progress */
 	struct semaphore dqoff_sem;		/* serialize quota_off() and quota_on() on device */
 	struct file *files[MAXQUOTAS];		/* fp's to quotafiles */
-	time_t inode_expire[MAXQUOTAS];		/* expiretime for inode-quota */
-	time_t block_expire[MAXQUOTAS];		/* expiretime for block-quota */
-	char rsquash[MAXQUOTAS];		/* for quotas threat root as any other user */
+	struct mem_dqinfo info[MAXQUOTAS];	/* Information for each quota type */
 };
 
 /*
@@ -657,7 +689,7 @@
 
 	struct block_device	*s_bdev;
 	struct list_head	s_mounts;	/* vfsmount(s) of this one */
-	struct quota_mount_options s_dquot;	/* Diskquota specific options */
+	struct quota_info	s_dquot;	/* Diskquota specific options */
 
 	union {
 		struct minix_sb_info	minix_sb;
@@ -803,9 +835,9 @@
 struct dquot_operations {
 	void (*initialize) (struct inode *, short);
 	void (*drop) (struct inode *);
-	int (*alloc_block) (struct inode *, unsigned long, char);
+	int (*alloc_space) (struct inode *, qsize_t, char);
 	int (*alloc_inode) (const struct inode *, unsigned long);
-	void (*free_block) (struct inode *, unsigned long);
+	void (*free_space) (struct inode *, qsize_t);
 	void (*free_inode) (const struct inode *, unsigned long);
 	int (*transfer) (struct inode *, struct iattr *);
 };
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/linux/quota.h linux-2.4.0-test11-newquota/include/linux/quota.h
--- linux-2.4.0-test11-quotafix/include/linux/quota.h	Mon Nov 27 09:43:43 2000
+++ linux-2.4.0-test11-newquota/include/linux/quota.h	Mon Nov 27 10:13:33 2000
@@ -40,30 +40,10 @@
 #define _LINUX_QUOTA_
 
 #include <linux/errno.h>
+#include <linux/types.h>
 
-/*
- * Convert diskblocks to blocks and the other way around.
- */
-#define dbtob(num) (num << BLOCK_SIZE_BITS)
-#define btodb(num) (num >> BLOCK_SIZE_BITS)
-
-/*
- * Convert count of filesystem blocks to diskquota blocks, meant
- * for filesystems where i_blksize != BLOCK_SIZE
- */
-#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
-
-/*
- * Definitions for disk quotas imposed on the average user
- * (big brother finally hits Linux).
- *
- * The following constants define the amount of time given a user
- * before the soft limits are treated as hard limits (usually resulting
- * in an allocation failure). The timer is started when the user crosses
- * their soft limit, it is reset when they go below their soft limit.
- */
-#define MAX_IQ_TIME  604800	/* (7*24*60*60) 1 week */
-#define MAX_DQ_TIME  604800	/* (7*24*60*60) 1 week */
+typedef __kernel_uid32_t qid_t;	/* Type in which we store ids in memory */
+typedef __u64 qsize_t;		/* Type in which we store size limitations */
 
 #define MAXQUOTAS 2
 #define USRQUOTA  0		/* element used for user quotas */
@@ -76,11 +56,33 @@
 	"user",    /* USRQUOTA */ \
 	"group",   /* GRPQUOTA */ \
 	"undefined", \
-};
+}
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+	0xd9c01f11,	/* USRQUOTA */\
+	0xd9c01927	/* GRPQUOTA */\
+}
 
-#define QUOTAFILENAME "quota"
+#define INITQVERSIONS {\
+	0,		/* USRQUOTA */\
+	0		/* GRPQUOTA */\
+}
+
+#define QUOTAFILENAME "aquota"
 #define QUOTAGROUP "staff"
 
+/* Size of blocks in which are counted size limits */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+/* Conversion routines from and to quota blocks */
+#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
+#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+
 /*
  * Command definitions for the 'quotactl' system call.
  * The commands are broken into a main command defined below
@@ -93,43 +95,106 @@
 
 #define Q_QUOTAON  0x0100	/* enable quotas */
 #define Q_QUOTAOFF 0x0200	/* disable quotas */
-#define Q_GETQUOTA 0x0300	/* get limits and usage */
-#define Q_SETQUOTA 0x0400	/* set limits and usage */
-#define Q_SETUSE   0x0500	/* set usage */
+/* GETQUOTA, SETQUOTA and SETUSE which were at 0x0300-0x0500 has now other parameteres */
 #define Q_SYNC     0x0600	/* sync disk copy of a filesystems quotas */
 #define Q_SETQLIM  0x0700	/* set limits */
 #define Q_GETSTATS 0x0800	/* get collected stats */
-#define Q_RSQUASH  0x1000	/* set root_squash option */
+#define Q_GETINFO  0x0900	/* get info about quotas - graces, flags... */
+#define Q_SETINFO  0x0A00	/* set info about quotas */
+#define Q_SETGRACE 0x0B00	/* set inode and block grace */
+#define Q_SETFLAGS 0x0C00	/* set flags for quota */
+#define Q_GETQUOTA 0x0D00	/* get limits and usage */
+#define Q_SETQUOTA 0x0E00	/* set limits and usage */
+#define Q_SETUSE   0x0F00	/* set usage */
+/* 0x1000 used by old RSQUASH */
 
 /*
  * The following structure defines the format of the disk quota file
- * (as it appears on disk) - the file is an array of these structures
- * indexed by user or group number.
+ * (as it appears on disk) - the file is a hash table whose entries points
+ * to blocks of these structures.
  */
-struct dqblk {
-	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
-	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
-	__u32 dqb_curblocks;	/* current block count */
+struct disk_dqblk {
+	__u32 dqb_id;		/* id this quota applies to */
 	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
 	__u32 dqb_isoftlimit;	/* preferred inode limit */
 	__u32 dqb_curinodes;	/* current # allocated inodes */
-	time_t dqb_btime;		/* time limit for excessive disk use */
-	time_t dqb_itime;		/* time limit for excessive inode use */
+	__u32 dqb_bhardlimit;	/* absolute limit on disk space (in QUOTABLOCK_SIZE) */
+	__u32 dqb_bsoftlimit;	/* preferred limit on disk space (in QUOTABLOCK_SIZE) */
+	__u64 dqb_curspace;	/* current space occupied (in bytes) */
+	__u64 dqb_btime;	/* time limit for excessive disk use */
+	__u64 dqb_itime;	/* time limit for excessive inode use */
+};
+
+/* This is in-memory copy of quota block. See meaning of entries above */
+struct mem_dqblk {
+	unsigned int dqb_ihardlimit;
+	unsigned int dqb_isoftlimit;
+	unsigned int dqb_curinodes;
+	unsigned int dqb_bhardlimit;
+	unsigned int dqb_bsoftlimit;
+	qsize_t dqb_curspace;
+	__kernel_time_t dqb_btime;
+	__kernel_time_t dqb_itime;
 };
 
 /*
- * Shorthand notation.
+ * Here are header structures as written on disk and their in-memory copies
  */
-#define	dq_bhardlimit	dq_dqb.dqb_bhardlimit
-#define	dq_bsoftlimit	dq_dqb.dqb_bsoftlimit
-#define	dq_curblocks	dq_dqb.dqb_curblocks
-#define	dq_ihardlimit	dq_dqb.dqb_ihardlimit
-#define	dq_isoftlimit	dq_dqb.dqb_isoftlimit
-#define	dq_curinodes	dq_dqb.dqb_curinodes
-#define	dq_btime	dq_dqb.dqb_btime
-#define	dq_itime	dq_dqb.dqb_itime
+/* First generic header */
+struct disk_dqheader {
+	__u32 dqh_magic;	/* Magic number identifying file */
+	__u32 dqh_version;	/* File version */
+};
+
+/* Header with type and version specific information */
+struct disk_dqinfo {
+	__u32 dqi_bgrace;	/* Time before block soft limit becomes hard limit */
+	__u32 dqi_igrace;	/* Time before inode soft limit becomes hard limit */
+	__u32 dqi_flags;	/* Flags for quotafile (DQF_*) */
+	__u32 dqi_blocks;	/* Number of blocks in file */
+	__u32 dqi_free_blk;	/* Number of first free block in the list */
+	__u32 dqi_free_entry;	/* Number of block with at least one free entry */
+};
 
-#define dqoff(UID)      ((loff_t)((UID) * sizeof (struct dqblk)))
+/* Inmemory copy of version specific information */
+struct mem_dqinfo {
+	unsigned int dqi_bgrace;
+	unsigned int dqi_igrace;
+	unsigned int dqi_flags;
+	unsigned int dqi_blocks;
+	unsigned int dqi_free_blk;
+	unsigned int dqi_free_entry;
+};
+
+/* Flags for version specific files */
+#define DQF_MASK  0x0000	/* Mask for all valid ondisk flags */
+
+#ifdef __KERNEL__
+#define DQF_DIRTY 0x0010	/* Is info dirty? */
+extern inline void mark_info_dirty(struct mem_dqinfo *info)
+{
+	info->dqi_flags |= DQF_DIRTY;
+}
+#define info_dirty(info) ((info)->dqi_flags & DQF_DIRTY)
+#endif
+/*
+ *  Structure of header of block with quota structures. It is padded to 16 bytes so
+ *  there will be space for exactly 18 quota-entries in a block
+ */
+struct disk_dqdbheader {
+	__u32 dqdh_next_free;	/* Number of next block with free entry */
+	__u32 dqdh_prev_free;	/* Number of previous block with free entry */
+	__u16 dqdh_entries;	/* Number of valid entries in block */
+	__u16 dqdh_pad1;
+	__u32 dqdh_pad2;
+};
+
+#define DQINFOOFF	sizeof(struct disk_dqheader)	/* Offset of info header in file */
+#define DQBLKSIZE_BITS	10
+#define DQBLKSIZE	(1 << DQBLKSIZE_BITS)	/* Size of block with quota structures */
+#define DQTREEOFF	1		/* Offset of tree in file in blocks */
+#define DQTREEDEPTH	4		/* Depth of quota tree */
+#define DQSTRINBLK	((DQBLKSIZE - sizeof(struct disk_dqdbheader)) / sizeof(struct disk_dqblk))	/* Number of entries in one blocks */
 
 struct dqstats {
 	__u32 lookups;
@@ -145,7 +210,6 @@
 #ifdef __KERNEL__
 
 extern int nr_dquots, nr_free_dquots;
-extern int dquot_root_squash;
 
 #define NR_DQHASH 43            /* Just an arbitrary number */
 
@@ -165,33 +229,52 @@
 
 	/* fields after this point are cleared when invalidating */
 	struct super_block *dq_sb;	/* superblock this applies to */
-	unsigned int dq_id;		/* ID this applies to (uid, gid) */
+	qid_t dq_id;			/* ID this applies to (uid, gid) */
 	kdev_t dq_dev;			/* Device this applies to */
 	short dq_type;			/* Type of quota */
 	short dq_flags;			/* See DQ_* */
+	loff_t dq_off;			/* Offset of structure in file (0 for not allocated) */
 	unsigned long dq_referenced;	/* Number of times this dquot was 
 					   referenced during its lifetime */
-	struct dqblk dq_dqb;		/* Diskquota usage */
+	struct mem_dqblk dq_dqb;		/* Diskquota usage */
 };
 
 #define NODQUOT (struct dquot *)NULL
 
+#define dq_curspace dq_dqb.dqb_curspace
+#define dq_curinodes dq_dqb.dqb_curinodes
+#define dq_isoftlimit dq_dqb.dqb_isoftlimit
+#define dq_ihardlimit dq_dqb.dqb_ihardlimit
+#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
+#define dq_bhardlimit dq_dqb.dqb_bhardlimit
+#define dq_itime dq_dqb.dqb_itime
+#define dq_btime dq_dqb.dqb_btime
+
 /*
  * Flags used for set_dqblk.
  */
-#define SET_QUOTA         0x02
-#define SET_USE           0x04
-#define SET_QLIMIT        0x08
+#define SET_QUOTA         0x01
+#define SET_USE           0x02
+#define SET_QLIMIT        0x04
+
+/*
+ * Flags used for set_info
+ */
+#define ISET_GRACE 0x01
+#define ISET_FLAGS 0x02
+#define ISET_ALL   0x03
 
 #define QUOTA_OK          0
 #define NO_QUOTA          1
 
+typedef char *dqbuf_t;
+
 #else
 
 # /* nodep */ include <sys/cdefs.h>
 
 __BEGIN_DECLS
-long quotactl __P ((int, const char *, int, caddr_t));
+long quotactl __P ((int, const char *, qid_t, __kernel_caddr_t));
 __END_DECLS
 
 #endif /* __KERNEL__ */
diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/linux/quotaops.h linux-2.4.0-test11-newquota/include/linux/quotaops.h
--- linux-2.4.0-test11-quotafix/include/linux/quotaops.h	Mon Nov 27 09:43:43 2000
+++ linux-2.4.0-test11-newquota/include/linux/quotaops.h	Sun Dec  3 21:15:47 2000
@@ -26,10 +26,10 @@
 extern int  quota_off(struct super_block *sb, short type);
 extern int  sync_dquots(kdev_t dev, short type);
 
-extern int  dquot_alloc_block(struct inode *inode, unsigned long number, char prealloc);
+extern int  dquot_alloc_space(struct inode *inode, qsize_t number, char prealloc);
 extern int  dquot_alloc_inode(const struct inode *inode, unsigned long number);
 
-extern void dquot_free_block(struct inode *inode, unsigned long number);
+extern void dquot_free_space(struct inode *inode, qsize_t number);
 extern void dquot_free_inode(const struct inode *inode, unsigned long number);
 
 extern int  dquot_transfer(struct inode *inode, struct iattr *iattr);
@@ -59,36 +59,36 @@
 	unlock_kernel();
 }
 
-extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr)
+extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
 	if (sb_any_quota_enabled(inode->i_sb)) {
 		/* Number of used blocks is updated in alloc_block() */
-		if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) {
+		if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) {
 			unlock_kernel();
 			return 1;
 		}
 	}
 	else {
-		inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+		inode_add_bytes(inode, nr);
 		mark_inode_dirty(inode);
 	}
 	unlock_kernel();
 	return 0;
 }
 
-extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr)
+extern __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
 	if (sb_any_quota_enabled(inode->i_sb)) {
 		/* Number of used blocks is updated in alloc_block() */
-		if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) {
+		if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) {
 			unlock_kernel();
 			return 1;
 		}
 	}
 	else {
-		inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+		inode_add_bytes(inode, nr);
 		mark_inode_dirty(inode);
 	}
 	unlock_kernel();
@@ -109,13 +109,13 @@
 	return 0;
 }
 
-extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr)
+extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
 	if (sb_any_quota_enabled(inode->i_sb))
-		inode->i_sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize));
+		inode->i_sb->dq_op->free_space(inode, nr);
 	else {
-		inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+		inode_sub_bytes(inode, nr);
 		mark_inode_dirty(inode);
 	}
 	unlock_kernel();
@@ -158,31 +158,36 @@
 #define DQUOT_SYNC(dev)				do { } while(0)
 #define DQUOT_OFF(sb)				do { } while(0)
 #define DQUOT_TRANSFER(inode, iattr)		(0)
-extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr)
+extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
-	inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	inode_add_bytes(inode, nr);
 	unlock_kernel();
 	mark_inode_dirty(inode);
 	return 0;
 }
 
-extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr)
+extern __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
-	inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	inode_add_bytes(inode, nr);
 	unlock_kernel();
 	mark_inode_dirty(inode);
 	return 0;
 }
 
-extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr)
+extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
-	inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+	inode_sub_bytes(inode, nr);
 	unlock_kernel();
 	mark_inode_dirty(inode);
 }
 
 #endif /* CONFIG_QUOTA */
+
+#define DQUOT_ALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_PREALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_FREE_BLOCK(inode, nr) DQUOT_FREE_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+
 #endif /* _LINUX_QUOTAOPS_ */
