Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.4.13-ac8 /fs/dquot.c

Filename:/fs/dquot.c
Lines Added:852
Lines Deleted:182
Also changed in: (Previous) 2.4.13-ac7  2.4.13-ac6  2.4.13-ac5  2.4.13-ac4  2.4.13-ac3  2.4.13-ac1 
(Following) 2.4.14  2.4.15-greased-turkey  2.4.18-pre3-ac1  2.4.18-pre3-ac2  2.4.18-pre7-ac1  2.4.18-pre7-ac2 

Location
[  2.4.13-ac8
  [  fs
     o  dquot.c

Patch

diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/dquot.c linux.ac/fs/dquot.c
--- linux.vanilla/fs/dquot.c   Thu Oct 11 13:52:13 2001
+++ linux.ac/fs/dquot.c   Mon Oct 29 13:31:56 2001
@@ -35,7 +35,7 @@
  *      Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99
  *
  *      Used struct list_head instead of own list struct
- *      Invalidation of dquots with dq_count > 0 no longer possible
+ *      Invalidation of referenced dquots is no longer possible
  *      Improved free_dquots list management
  *      Quota and i_blocks are now updated in one place to avoid races
  *      Warnings are now delayed so we won't block in critical section
@@ -45,6 +45,10 @@
  *      Added dynamic quota structure allocation
  *      Jan Kara <jack@suse.cz> 12/2000
  *
+ *      New quotafile format
+ *      Alocation units changed to bytes
+ *      Jan Kara, <jack@suse.cz>, 2000
+ *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
 
@@ -63,14 +67,19 @@
 #include <linux/init.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_NUM_VERSION__   (6*10000+5*100+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;
 }
@@ -120,7 +129,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:
@@ -136,6 +145,26 @@
    return is_enabled(sb_dqopt(sb), type);
 }
 
+static inline void get_dquot_ref(struct dquot *dquot)
+{
+   dquot->dq_count++;
+}
+
+static inline void put_dquot_ref(struct dquot *dquot)
+{
+   dquot->dq_count--;
+}
+
+static inline void get_dquot_dup_ref(struct dquot *dquot)
+{
+   dquot->dq_dup_ref++;
+}
+
+static inline void put_dquot_dup_ref(struct dquot *dquot)
+{
+   dquot->dq_dup_ref--;
+}
+
 static inline int const hashfn(kdev_t dev, unsigned int id, short type)
 {
    return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
@@ -189,11 +218,6 @@
 
 static inline void remove_free_dquot(struct dquot *dquot)
 {
-   /* sanity check */
-   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 */
-   }
    list_del(&dquot->dq_free);
    INIT_LIST_HEAD(&dquot->dq_free);
    nr_free_dquots--;
@@ -246,6 +270,7 @@
    wake_up(&dquot->dq_wait_lock);
 }
 
+/* Wait for dquot to be unused */
 static void __wait_dquot_unused(struct dquot *dquot)
 {
    DECLARE_WAITQUEUE(wait, current);
@@ -261,6 +286,394 @@
    current->state = TASK_RUNNING;
 }
 
+/* Wait for all duplicated dquot references to be dropped */
+static void __wait_dup_drop(struct dquot *dquot)
+{
+   DECLARE_WAITQUEUE(wait, current);
+
+   add_wait_queue(&dquot->dq_wait_free, &wait);
+repeat:
+   set_current_state(TASK_UNINTERRUPTIBLE);
+   if (dquot->dq_dup_ref) {
+      schedule();
+      goto repeat;
+   }
+   remove_wait_queue(&dquot->dq_wait_free, &wait);
+   current->state = TASK_RUNNING;
+}
+
+/*
+ *   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...
  */
@@ -271,32 +684,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 &g+
t;> 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)
@@ -305,31 +885,58 @@
    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)
+{
+   /* 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);
+}
+
+/*
+ *   End of IO functions
+ */
+
 /*
  * Unhash and selectively clear the dquot structure,
  * but preserve the use count, list pointers, and
@@ -344,8 +951,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
@@ -363,6 +971,7 @@
          continue;
       if (dquot->dq_type != type)
          continue;
+      dquot->dq_flags |= DQ_INVAL;
       if (dquot->dq_count)
          /*
           *  Wait for any users of quota. As we have already cleared the flags in
@@ -395,12 +1004,14 @@
          continue;
       if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED)))
          continue;
-      /* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */
-      dquot->dq_count++;
+      /* Get reference to quota so it won't be invalidated. get_dquot_ref()
+       * is enough since if dquot is locked/modified it can't be
+       * on the free list */
+      get_dquot_ref(dquot);
       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;
    }
@@ -426,18 +1037,21 @@
    }
 }
 
-int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
+void shrink_dqcache_memory(int priority, unsigned int gfp_mask)
 {
    prune_dqcache(nr_free_dquots / (priority + 1));
    kmem_cache_shrink(dquot_cachep);
-   return 0;
 }
 
-/* NOTE: If you change this function please check whether dqput_blocks() works right... */
+/*
+ * Put reference to dquot
+ * NOTE: If you change this function please check whether dqput_blocks() works right...
+ */
 static void dqput(struct dquot *dquot)
 {
    if (!dquot)
       return;
+#ifdef __DQUOT_PARANOIA
    if (!dquot->dq_count) {
       printk("VFS: dqput: trying to free free dquot\n");
       printk("VFS: device %s, dquot of %s %d\n",
@@ -445,28 +1059,36 @@
          dquot->dq_id);
       return;
    }
+#endif
 
    dqstats.drops++;
 we_slept:
+   if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) {   /* Last unduplicated reference? */
+      __wait_dup_drop(dquot);
+      goto we_slept;
+   }
    if (dquot->dq_count > 1) {
       /* We have more than one user... We can simply decrement use count */
-      dquot->dq_count--;
+      put_dquot_ref(dquot);
       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... */
+      put_dquot_ref(dquot);
       return;
    }
-   dquot->dq_count--;
-   /* Place at end of LRU free queue */
-   put_dquot_last(dquot);
+#endif
+   put_dquot_ref(dquot);
+   /* Don't put dquot on free list if it will be invalidated (to avoid races between invalidate_dquots() and prune_dqcache()) */
+   if (!(dquot->dq_flags & DQ_INVAL))
+      /* Place at end of LRU free queue */
+      put_dquot_last(dquot);
    wake_up(&dquot->dq_wait_free);
 }
 
@@ -495,7 +1117,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)) {
@@ -519,42 +1141,65 @@
       insert_dquot_hash(dquot);
            read_dquot(dquot);
    } else {
-      if (!dquot->dq_count++)
+      if (!dquot->dq_count)
          remove_free_dquot(dquot);
+      get_dquot_ref(dquot);
       dqstats.cache_hits++;
       wait_on_dquot(dquot);
       if (empty)
          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++;
 
    return dquot;
 }
 
+/* Duplicate reference to dquot got from inode */
 static struct dquot *dqduplicate(struct dquot *dquot)
 {
    if (dquot == NODQUOT)
       return NODQUOT;
-   dquot->dq_count++;
+   get_dquot_ref(dquot);
+#ifdef __DQUOT_PARANOIA
    if (!dquot->dq_sb) {
       printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
-      dquot->dq_count--;
+      put_dquot_ref(dquot);
       return NODQUOT;
    }
    if (dquot->dq_flags & DQ_LOCKED)
       printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
+#endif
+   get_dquot_dup_ref(dquot);
    dquot->dq_referenced++;
    dqstats.lookups++;
    return dquot;
 }
 
+/* Put duplicated reference */
+static void dqputduplicate(struct dquot *dquot)
+{
+#ifdef __DQUOT_PARANOIA
+   if (!dquot->dq_dup_ref) {
+      printk(KERN_ERR "VFS: dqputduplicate(): Duplicated dquot put without duplicate reference.\n");
+      return;
+   }
+#endif
+   put_dquot_dup_ref(dquot);
+   if (!dquot->dq_dup_ref)
+      wake_up(&dquot->dq_wait_free);
+   put_dquot_ref(dquot);
+   dqstats.drops++;
+}
+
 static int dqinit_needed(struct inode *inode, short type)
 {
    int cnt;
@@ -586,6 +1231,7 @@
          struct dentry *dentry = dget(filp->f_dentry);
          file_list_unlock();
          sb->dq_op->initialize(inode, type);
+         inode->i_flags |= S_QUOTA;
          dput(dentry);
          mntput(mnt);
          /* As we may have blocked we had better restart... */
@@ -598,8 +1244,10 @@
 /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
 static inline int dqput_blocks(struct dquot *dquot)
 {
-   if (dquot->dq_count == 1)
+   if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1)
       return 1;
+   if (dquot->dq_count <= 1) 
+      return 1; 
    return 0;
 }
 
@@ -652,9 +1300,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;
 }
 
@@ -670,13 +1318,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;
@@ -717,7 +1365,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";
@@ -752,7 +1400,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)
@@ -780,20 +1428,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;
@@ -801,7 +1449,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)
@@ -810,11 +1458,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
          /*
@@ -831,18 +1479,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;
@@ -853,22 +1501,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 &&
@@ -883,10 +1531,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))
@@ -895,10 +1543,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;
@@ -919,46 +1567,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.
@@ -968,7 +1618,7 @@
 void dquot_initialize(struct inode *inode, short type)
 {
    struct dquot *dquot[MAXQUOTAS];
-   unsigned int id = 0;
+   qid_t id = 0;
    short cnt;
 
    if (IS_NOQUOTA(inode))
@@ -1017,6 +1667,7 @@
    struct dquot *dquot;
    short cnt;
 
+   /* Here we needn't be afraid of races as inode is not used any more */
    inode->i_flags &= ~S_QUOTA;
    for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
       if (inode->i_dquot[cnt] == NODQUOT)
@@ -1030,7 +1681,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];
@@ -1045,22 +1696,22 @@
       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);
-   /* NOBLOCK End */
+   inode_add_bytes(inode, number);
+        /* NOBLOCK End */
    ret = QUOTA_OK;
 warn_put_all:
    flush_warnings(dquot, warntype);
    for (cnt = 0; cnt < MAXQUOTAS; cnt++)
       if (dquot[cnt] != NODQUOT)
-         dqput(dquot[cnt]);
+         dqputduplicate(dquot[cnt]);
    return ret;
 }
 
@@ -1097,14 +1748,14 @@
    flush_warnings(dquot, warntype);
    for (cnt = 0; cnt < MAXQUOTAS; cnt++)
       if (dquot[cnt] != NODQUOT)
-         dqput(dquot[cnt]);
+         dqputduplicate(dquot[cnt]);
    return ret;
 }
 
 /*
  * 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;
@@ -1114,10 +1765,10 @@
       dquot = dqduplicate(inode->i_dquot[cnt]);
       if (dquot == NODQUOT)
          continue;
-      dquot_decr_blocks(dquot, number);
-      dqput(dquot);
+      dquot_decr_space(dquot, number);
+      dqputduplicate(dquot);
    }
-   inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9);
+   inode_sub_bytes(inode, number);
    /* NOBLOCK End */
 }
 
@@ -1135,7 +1786,7 @@
       if (dquot == NODQUOT)
          continue;
       dquot_decr_inodes(dquot, number);
-      dqput(dquot);
+      dqputduplicate(dquot);
    }
    /* NOBLOCK End */
 }
@@ -1147,7 +1798,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,
@@ -1177,7 +1828,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... */
@@ -1187,7 +1838,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;
    }
 
@@ -1202,10 +1853,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();
@@ -1222,7 +1873,7 @@
    flush_warnings(transfer_to, warntype);
    for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
       if (transfer_to[cnt] != NODQUOT)
-         dqput(transfer_to[cnt]);
+         dqputduplicate(transfer_to[cnt]);
       if (transfer_from[cnt] != NODQUOT)
          dqput(transfer_from[cnt]);
    }
@@ -1235,6 +1886,7 @@
 
    for (i = 0; i < NR_DQHASH; i++)
       INIT_LIST_HEAD(dquot_hash + i);
+   dqstats.version = __DQUOT_NUM_VERSION__;
    printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
    return 0;
 }
@@ -1246,14 +1898,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:
@@ -1265,7 +1917,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:
@@ -1287,7 +1939,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;
@@ -1304,11 +1956,12 @@
       /* Note: these are blocking operations */
       remove_dquot_ref(sb, cnt);
       invalidate_dquots(sb, 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);
@@ -1316,20 +1969,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;
 
@@ -1356,26 +2020,23 @@
    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;
    /* We don't want quota on quota files */
    dquot_drop(inode);
    inode->i_flags |= S_NOQUOTA;
 
    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:
@@ -1390,7 +2051,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;
@@ -1401,15 +2062,16 @@
    cmds = cmd >> SUBCMDSHIFT;
    type = cmd & SUBCMDMASK;
 
-   if ((u_int) type >= MAXQUOTAS)
-      goto out;
-   if (id & ~0xFFFF)
+
+   if ((uint) type >= MAXQUOTAS || cmds > 0x1100 || cmds < 0x100 || cmds == 0x0300 ||
+       cmds == 0x0400 || cmds == 0x0500 || cmds == 0x1000)
       goto out;
 
    ret = -EPERM;
    switch (cmds) {
       case Q_SYNC:
       case Q_GETSTATS:
+      case Q_GETINFO:
          break;
       case Q_GETQUOTA:
          if (((type == USRQUOTA && current->euid != id) ||
@@ -1422,7 +2084,6 @@
             goto out;
    }
 
-   ret = -EINVAL;
    dev = NODEV;
    if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
       mode_t mode;
@@ -1454,7 +2115,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;
@@ -1471,16 +2132,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:
    if (sb)
       drop_super(sb);


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.