fs/affs/file.c: use BUG_ON
[safe/jmp/linux-2.6] / fs / dquot.c
index 0a5febc..dfba162 100644 (file)
@@ -69,7 +69,6 @@
 #include <linux/file.h>
 #include <linux/slab.h>
 #include <linux/sysctl.h>
-#include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/capability.h>
 #include <linux/quotaops.h>
 #include <linux/writeback.h> /* for inode_lock, oddly enough.. */
+#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#endif
 
 #include <asm/uaccess.h>
 
@@ -286,7 +289,15 @@ static void wait_on_dquot(struct dquot *dquot)
        mutex_unlock(&dquot->dq_lock);
 }
 
-#define mark_dquot_dirty(dquot) ((dquot)->dq_sb->dq_op->mark_dirty(dquot))
+static inline int dquot_dirty(struct dquot *dquot)
+{
+       return test_bit(DQ_MOD_B, &dquot->dq_flags);
+}
+
+static inline int mark_dquot_dirty(struct dquot *dquot)
+{
+       return dquot->dq_sb->dq_op->mark_dirty(dquot);
+}
 
 int dquot_mark_dquot_dirty(struct dquot *dquot)
 {
@@ -475,7 +486,7 @@ int vfs_quota_sync(struct super_block *sb, int type)
                spin_lock(&dq_list_lock);
                dirty = &dqopt->info[cnt].dqi_dirty_list;
                while (!list_empty(dirty)) {
-                       dquot = list_entry(dirty->next, struct dquot, dq_dirty);
+                       dquot = list_first_entry(dirty, struct dquot, dq_dirty);
                        /* Dirty and inactive can be only bad dquot... */
                        if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
                                clear_dquot_dirty(dquot);
@@ -539,6 +550,11 @@ static int shrink_dqcache_memory(int nr, gfp_t gfp_mask)
        return (dqstats.free_dquots / 100) * sysctl_vfs_cache_pressure;
 }
 
+static struct shrinker dqcache_shrinker = {
+       .shrink = shrink_dqcache_memory,
+       .seeks = DEFAULT_SEEKS,
+};
+
 /*
  * Put reference to dquot
  * NOTE: If you change this function please check whether dqput_blocks() works right...
@@ -688,9 +704,8 @@ static int dqinit_needed(struct inode *inode, int type)
 /* This routine is guarded by dqonoff_mutex mutex */
 static void add_dquot_ref(struct super_block *sb, int type)
 {
-       struct inode *inode;
+       struct inode *inode, *old_inode = NULL;
 
-restart:
        spin_lock(&inode_lock);
        list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
                if (!atomic_read(&inode->i_writecount))
@@ -703,12 +718,18 @@ restart:
                __iget(inode);
                spin_unlock(&inode_lock);
 
+               iput(old_inode);
                sb->dq_op->initialize(inode, type);
-               iput(inode);
-               /* As we may have blocked we had better restart... */
-               goto restart;
+               /* We hold a reference to 'inode' so it couldn't have been
+                * removed from s_inodes list while we dropped the inode_lock.
+                * We cannot iput the inode now as we can be holding the last
+                * reference and we cannot iput it under inode_lock. So we
+                * keep the reference and iput it later. */
+               old_inode = inode;
+               spin_lock(&inode_lock);
        }
        spin_unlock(&inode_lock);
+       iput(old_inode);
 }
 
 /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
@@ -721,7 +742,8 @@ static inline int dqput_blocks(struct dquot *dquot)
 
 /* Remove references to dquots from inode - add dquot to list for freeing if needed */
 /* We can't race with anybody because we hold dqptr_sem for writing... */
-int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
+static int remove_inode_dquot_ref(struct inode *inode, int type,
+                                 struct list_head *tofree_head)
 {
        struct dquot *dquot = inode->i_dquot[type];
 
@@ -818,6 +840,19 @@ static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
        clear_bit(DQ_BLKS_B, &dquot->dq_flags);
 }
 
+static int warning_issued(struct dquot *dquot, const int warntype)
+{
+       int flag = (warntype == QUOTA_NL_BHARDWARN ||
+               warntype == QUOTA_NL_BSOFTLONGWARN) ? DQ_BLKS_B :
+               ((warntype == QUOTA_NL_IHARDWARN ||
+               warntype == QUOTA_NL_ISOFTLONGWARN) ? DQ_INODES_B : 0);
+
+       if (!flag)
+               return 0;
+       return test_and_set_bit(flag, &dquot->dq_flags);
+}
+
+#ifdef CONFIG_PRINT_QUOTA_WARNING
 static int flag_print_warnings = 1;
 
 static inline int need_print_warning(struct dquot *dquot)
@@ -834,24 +869,13 @@ static inline int need_print_warning(struct dquot *dquot)
        return 0;
 }
 
-/* Values of warnings */
-#define NOWARN 0
-#define IHARDWARN 1
-#define ISOFTLONGWARN 2
-#define ISOFTWARN 3
-#define BHARDWARN 4
-#define BSOFTLONGWARN 5
-#define BSOFTWARN 6
-
 /* Print warning to user which exceeded quota */
-static void print_warning(struct dquot *dquot, const char warntype)
+static void print_warning(struct dquot *dquot, const int warntype)
 {
        char *msg = NULL;
        struct tty_struct *tty;
-       int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS_B :
-         ((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES_B : 0);
 
-       if (!need_print_warning(dquot) || (flag && test_and_set_bit(flag, &dquot->dq_flags)))
+       if (!need_print_warning(dquot))
                return;
 
        mutex_lock(&tty_mutex);
@@ -859,28 +883,28 @@ static void print_warning(struct dquot *dquot, const char warntype)
        if (!tty)
                goto out_lock;
        tty_write_message(tty, dquot->dq_sb->s_id);
-       if (warntype == ISOFTWARN || warntype == BSOFTWARN)
+       if (warntype == QUOTA_NL_ISOFTWARN || warntype == QUOTA_NL_BSOFTWARN)
                tty_write_message(tty, ": warning, ");
        else
                tty_write_message(tty, ": write failed, ");
        tty_write_message(tty, quotatypes[dquot->dq_type]);
        switch (warntype) {
-               case IHARDWARN:
+               case QUOTA_NL_IHARDWARN:
                        msg = " file limit reached.\r\n";
                        break;
-               case ISOFTLONGWARN:
+               case QUOTA_NL_ISOFTLONGWARN:
                        msg = " file quota exceeded too long.\r\n";
                        break;
-               case ISOFTWARN:
+               case QUOTA_NL_ISOFTWARN:
                        msg = " file quota exceeded.\r\n";
                        break;
-               case BHARDWARN:
+               case QUOTA_NL_BHARDWARN:
                        msg = " block limit reached.\r\n";
                        break;
-               case BSOFTLONGWARN:
+               case QUOTA_NL_BSOFTLONGWARN:
                        msg = " block quota exceeded too long.\r\n";
                        break;
-               case BSOFTWARN:
+               case QUOTA_NL_BSOFTWARN:
                        msg = " block quota exceeded.\r\n";
                        break;
        }
@@ -888,14 +912,93 @@ static void print_warning(struct dquot *dquot, const char warntype)
 out_lock:
        mutex_unlock(&tty_mutex);
 }
+#endif
 
-static inline void flush_warnings(struct dquot **dquots, char *warntype)
+#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
+
+/* Netlink family structure for quota */
+static struct genl_family quota_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = 0,
+       .name = "VFS_DQUOT",
+       .version = 1,
+       .maxattr = QUOTA_NL_A_MAX,
+};
+
+/* Send warning to userspace about user which exceeded quota */
+static void send_warning(const struct dquot *dquot, const char warntype)
+{
+       static atomic_t seq;
+       struct sk_buff *skb;
+       void *msg_head;
+       int ret;
+       int msg_size = 4 * nla_total_size(sizeof(u32)) +
+                      2 * nla_total_size(sizeof(u64));
+
+       /* We have to allocate using GFP_NOFS as we are called from a
+        * filesystem performing write and thus further recursion into
+        * the fs to free some data could cause deadlocks. */
+       skb = genlmsg_new(msg_size, GFP_NOFS);
+       if (!skb) {
+               printk(KERN_ERR
+                 "VFS: Not enough memory to send quota warning.\n");
+               return;
+       }
+       msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+                       &quota_genl_family, 0, QUOTA_NL_C_WARNING);
+       if (!msg_head) {
+               printk(KERN_ERR
+                 "VFS: Cannot store netlink header in quota warning.\n");
+               goto err_out;
+       }
+       ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, dquot->dq_type);
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, dquot->dq_id);
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR,
+               MAJOR(dquot->dq_sb->s_dev));
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR,
+               MINOR(dquot->dq_sb->s_dev));
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current->user->uid);
+       if (ret)
+               goto attr_err_out;
+       genlmsg_end(skb, msg_head);
+
+       ret = genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS);
+       if (ret < 0 && ret != -ESRCH)
+               printk(KERN_ERR
+                       "VFS: Failed to send notification message: %d\n", ret);
+       return;
+attr_err_out:
+       printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
+err_out:
+       kfree_skb(skb);
+}
+#endif
+
+static inline void flush_warnings(struct dquot * const *dquots, char *warntype)
 {
        int i;
 
        for (i = 0; i < MAXQUOTAS; i++)
-               if (dquots[i] != NODQUOT && warntype[i] != NOWARN)
+               if (dquots[i] != NODQUOT && warntype[i] != QUOTA_NL_NOWARN &&
+                   !warning_issued(dquots[i], warntype[i])) {
+#ifdef CONFIG_PRINT_QUOTA_WARNING
                        print_warning(dquots[i], warntype[i]);
+#endif
+#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
+                       send_warning(dquots[i], warntype[i]);
+#endif
+               }
 }
 
 static inline char ignore_hardlimit(struct dquot *dquot)
@@ -909,14 +1012,14 @@ static inline char ignore_hardlimit(struct dquot *dquot)
 /* needs dq_data_lock */
 static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
 {
-       *warntype = NOWARN;
+       *warntype = QUOTA_NL_NOWARN;
        if (inodes <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
                return QUOTA_OK;
 
        if (dquot->dq_dqb.dqb_ihardlimit &&
           (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit &&
             !ignore_hardlimit(dquot)) {
-               *warntype = IHARDWARN;
+               *warntype = QUOTA_NL_IHARDWARN;
                return NO_QUOTA;
        }
 
@@ -924,14 +1027,14 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
           (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
            dquot->dq_dqb.dqb_itime && get_seconds() >= dquot->dq_dqb.dqb_itime &&
             !ignore_hardlimit(dquot)) {
-               *warntype = ISOFTLONGWARN;
+               *warntype = QUOTA_NL_ISOFTLONGWARN;
                return NO_QUOTA;
        }
 
        if (dquot->dq_dqb.dqb_isoftlimit &&
           (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
            dquot->dq_dqb.dqb_itime == 0) {
-               *warntype = ISOFTWARN;
+               *warntype = QUOTA_NL_ISOFTWARN;
                dquot->dq_dqb.dqb_itime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
        }
 
@@ -941,7 +1044,7 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
 /* needs dq_data_lock */
 static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
 {
-       *warntype = 0;
+       *warntype = QUOTA_NL_NOWARN;
        if (space <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
                return QUOTA_OK;
 
@@ -949,7 +1052,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
           toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
             !ignore_hardlimit(dquot)) {
                if (!prealloc)
-                       *warntype = BHARDWARN;
+                       *warntype = QUOTA_NL_BHARDWARN;
                return NO_QUOTA;
        }
 
@@ -958,7 +1061,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
            dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime &&
             !ignore_hardlimit(dquot)) {
                if (!prealloc)
-                       *warntype = BSOFTLONGWARN;
+                       *warntype = QUOTA_NL_BSOFTLONGWARN;
                return NO_QUOTA;
        }
 
@@ -966,7 +1069,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
           toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
            dquot->dq_dqb.dqb_btime == 0) {
                if (!prealloc) {
-                       *warntype = BSOFTWARN;
+                       *warntype = QUOTA_NL_BSOFTWARN;
                        dquot->dq_dqb.dqb_btime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
                }
                else
@@ -1061,7 +1164,7 @@ out_add:
                return QUOTA_OK;
        }
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-               warntype[cnt] = NOWARN;
+               warntype[cnt] = QUOTA_NL_NOWARN;
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
        if (IS_NOQUOTA(inode)) {        /* Now we can do reliable test... */
@@ -1107,7 +1210,7 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number)
        if (IS_NOQUOTA(inode))
                return QUOTA_OK;
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-               warntype[cnt] = NOWARN;
+               warntype[cnt] = QUOTA_NL_NOWARN;
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
        if (IS_NOQUOTA(inode)) {
                up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
@@ -1134,7 +1237,7 @@ warn_put_all:
                for (cnt = 0; cnt < MAXQUOTAS; cnt++)
                        if (inode->i_dquot[cnt])
                                mark_dquot_dirty(inode->i_dquot[cnt]);
-       flush_warnings((struct dquot **)inode->i_dquot, warntype);
+       flush_warnings(inode->i_dquot, warntype);
        up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
        return ret;
 }
@@ -1229,7 +1332,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
        /* Clear the arrays */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                transfer_to[cnt] = transfer_from[cnt] = NODQUOT;
-               warntype[cnt] = NOWARN;
+               warntype[cnt] = QUOTA_NL_NOWARN;
        }
        down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
        /* Now recheck reliably when holding dqptr_sem */
@@ -1346,31 +1449,43 @@ static inline void set_enable_flags(struct quota_info *dqopt, int type)
        switch (type) {
                case USRQUOTA:
                        dqopt->flags |= DQUOT_USR_ENABLED;
+                       dqopt->flags &= ~DQUOT_USR_SUSPENDED;
                        break;
                case GRPQUOTA:
                        dqopt->flags |= DQUOT_GRP_ENABLED;
+                       dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
                        break;
        }
 }
 
-static inline void reset_enable_flags(struct quota_info *dqopt, int type)
+static inline void reset_enable_flags(struct quota_info *dqopt, int type,
+                                     int remount)
 {
        switch (type) {
                case USRQUOTA:
                        dqopt->flags &= ~DQUOT_USR_ENABLED;
+                       if (remount)
+                               dqopt->flags |= DQUOT_USR_SUSPENDED;
+                       else
+                               dqopt->flags &= ~DQUOT_USR_SUSPENDED;
                        break;
                case GRPQUOTA:
                        dqopt->flags &= ~DQUOT_GRP_ENABLED;
+                       if (remount)
+                               dqopt->flags |= DQUOT_GRP_SUSPENDED;
+                       else
+                               dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
                        break;
        }
 }
 
+
 /*
  * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
  */
-int vfs_quota_off(struct super_block *sb, int type)
+int vfs_quota_off(struct super_block *sb, int type, int remount)
 {
-       int cnt;
+       int cnt, ret = 0;
        struct quota_info *dqopt = sb_dqopt(sb);
        struct inode *toputinode[MAXQUOTAS];
 
@@ -1380,9 +1495,17 @@ int vfs_quota_off(struct super_block *sb, int type)
                toputinode[cnt] = NULL;
                if (type != -1 && cnt != type)
                        continue;
+               /* If we keep inodes of quota files after remount and quotaoff
+                * is called, drop kept inodes. */
+               if (!remount && sb_has_quota_suspended(sb, cnt)) {
+                       iput(dqopt->files[cnt]);
+                       dqopt->files[cnt] = NULL;
+                       reset_enable_flags(dqopt, cnt, 0);
+                       continue;
+               }
                if (!sb_has_quota_enabled(sb, cnt))
                        continue;
-               reset_enable_flags(dqopt, cnt);
+               reset_enable_flags(dqopt, cnt, remount);
 
                /* Note: these are blocking operations */
                drop_dquot_ref(sb, cnt);
@@ -1398,7 +1521,8 @@ int vfs_quota_off(struct super_block *sb, int type)
                put_quota_format(dqopt->info[cnt].dqi_format);
 
                toputinode[cnt] = dqopt->files[cnt];
-               dqopt->files[cnt] = NULL;
+               if (!remount)
+                       dqopt->files[cnt] = NULL;
                dqopt->info[cnt].dqi_flags = 0;
                dqopt->info[cnt].dqi_igrace = 0;
                dqopt->info[cnt].dqi_bgrace = 0;
@@ -1421,19 +1545,26 @@ int vfs_quota_off(struct super_block *sb, int type)
                        /* If quota was reenabled in the meantime, we have
                         * nothing to do */
                        if (!sb_has_quota_enabled(sb, cnt)) {
-                               mutex_lock(&toputinode[cnt]->i_mutex);
+                               mutex_lock_nested(&toputinode[cnt]->i_mutex, I_MUTEX_QUOTA);
                                toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
                                  S_NOATIME | S_NOQUOTA);
                                truncate_inode_pages(&toputinode[cnt]->i_data, 0);
                                mutex_unlock(&toputinode[cnt]->i_mutex);
                                mark_inode_dirty(toputinode[cnt]);
-                               iput(toputinode[cnt]);
                        }
                        mutex_unlock(&dqopt->dqonoff_mutex);
+                       /* On remount RO, we keep the inode pointer so that we
+                        * can reenable quota on the subsequent remount RW.
+                        * But we have better not keep inode pointer when there
+                        * is pending delete on the quota file... */
+                       if (!remount)
+                               iput(toputinode[cnt]);
+                       else if (!toputinode[cnt]->i_nlink)
+                               ret = -EBUSY;
                }
        if (sb->s_bdev)
                invalidate_bdev(sb->s_bdev);
-       return 0;
+       return ret;
 }
 
 /*
@@ -1471,7 +1602,8 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
        invalidate_bdev(sb->s_bdev);
        mutex_lock(&inode->i_mutex);
        mutex_lock(&dqopt->dqonoff_mutex);
-       if (sb_has_quota_enabled(sb, type)) {
+       if (sb_has_quota_enabled(sb, type) ||
+                       sb_has_quota_suspended(sb, type)) {
                error = -EBUSY;
                goto out_lock;
        }
@@ -1494,6 +1626,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
 
        dqopt->ops[type] = fmt->qf_ops;
        dqopt->info[type].dqi_format = fmt;
+       dqopt->info[type].dqi_fmt_id = format_id;
        INIT_LIST_HEAD(&dqopt->info[type].dqi_dirty_list);
        mutex_lock(&dqopt->dqio_mutex);
        if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0) {
@@ -1529,25 +1662,55 @@ out_fmt:
        return error; 
 }
 
+/* Reenable quotas on remount RW */
+static int vfs_quota_on_remount(struct super_block *sb, int type)
+{
+       struct quota_info *dqopt = sb_dqopt(sb);
+       struct inode *inode;
+       int ret;
+
+       mutex_lock(&dqopt->dqonoff_mutex);
+       if (!sb_has_quota_suspended(sb, type)) {
+               mutex_unlock(&dqopt->dqonoff_mutex);
+               return 0;
+       }
+       BUG_ON(sb_has_quota_enabled(sb, type));
+
+       inode = dqopt->files[type];
+       dqopt->files[type] = NULL;
+       reset_enable_flags(dqopt, type, 0);
+       mutex_unlock(&dqopt->dqonoff_mutex);
+
+       ret = vfs_quota_on_inode(inode, type, dqopt->info[type].dqi_fmt_id);
+       iput(inode);
+
+       return ret;
+}
+
 /* Actual function called from quotactl() */
-int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
+int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
+                int remount)
 {
        struct nameidata nd;
        int error;
 
+       if (remount)
+               return vfs_quota_on_remount(sb, type);
+
        error = path_lookup(path, LOOKUP_FOLLOW, &nd);
        if (error < 0)
                return error;
-       error = security_quota_on(nd.dentry);
+       error = security_quota_on(nd.path.dentry);
        if (error)
                goto out_path;
        /* Quota file not on the same filesystem? */
-       if (nd.mnt->mnt_sb != sb)
+       if (nd.path.mnt->mnt_sb != sb)
                error = -EXDEV;
        else
-               error = vfs_quota_on_inode(nd.dentry->d_inode, type, format_id);
+               error = vfs_quota_on_inode(nd.path.dentry->d_inode, type,
+                                          format_id);
 out_path:
-       path_release(&nd);
+       path_put(&nd.path);
        return error;
 }
 
@@ -1613,10 +1776,19 @@ int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
 }
 
 /* Generic routine for setting common part of quota structure */
-static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
+static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
 {
        struct mem_dqblk *dm = &dquot->dq_dqb;
        int check_blim = 0, check_ilim = 0;
+       struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+
+       if ((di->dqb_valid & QIF_BLIMITS &&
+            (di->dqb_bhardlimit > dqi->dqi_maxblimit ||
+             di->dqb_bsoftlimit > dqi->dqi_maxblimit)) ||
+           (di->dqb_valid & QIF_ILIMITS &&
+            (di->dqb_ihardlimit > dqi->dqi_maxilimit ||
+             di->dqb_isoftlimit > dqi->dqi_maxilimit)))
+               return -ERANGE;
 
        spin_lock(&dq_data_lock);
        if (di->dqb_valid & QIF_SPACE) {
@@ -1648,7 +1820,7 @@ static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
                        clear_bit(DQ_BLKS_B, &dquot->dq_flags);
                }
                else if (!(di->dqb_valid & QIF_BTIME))  /* Set grace only if user hasn't provided his own... */
-                       dm->dqb_btime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+                       dm->dqb_btime = get_seconds() + dqi->dqi_bgrace;
        }
        if (check_ilim) {
                if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
@@ -1656,7 +1828,7 @@ static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
                        clear_bit(DQ_INODES_B, &dquot->dq_flags);
                }
                else if (!(di->dqb_valid & QIF_ITIME))  /* Set grace only if user hasn't provided his own... */
-                       dm->dqb_itime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+                       dm->dqb_itime = get_seconds() + dqi->dqi_igrace;
        }
        if (dm->dqb_bhardlimit || dm->dqb_bsoftlimit || dm->dqb_ihardlimit || dm->dqb_isoftlimit)
                clear_bit(DQ_FAKE_B, &dquot->dq_flags);
@@ -1664,21 +1836,24 @@ static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
                set_bit(DQ_FAKE_B, &dquot->dq_flags);
        spin_unlock(&dq_data_lock);
        mark_dquot_dirty(dquot);
+
+       return 0;
 }
 
 int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
 {
        struct dquot *dquot;
+       int rc;
 
        mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
        if (!(dquot = dqget(sb, id, type))) {
                mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
                return -ESRCH;
        }
-       do_set_dqblk(dquot, di);
+       rc = do_set_dqblk(dquot, di);
        dqput(dquot);
        mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
-       return 0;
+       return rc;
 }
 
 /* Generic routine for getting common part of quota file information */
@@ -1803,6 +1978,7 @@ static ctl_table fs_dqstats_table[] = {
                .mode           = 0444,
                .proc_handler   = &proc_dointvec,
        },
+#ifdef CONFIG_PRINT_QUOTA_WARNING
        {
                .ctl_name       = FS_DQ_WARNINGS,
                .procname       = "warnings",
@@ -1811,6 +1987,7 @@ static ctl_table fs_dqstats_table[] = {
                .mode           = 0644,
                .proc_handler   = &proc_dointvec,
        },
+#endif
        { .ctl_name = 0 },
 };
 
@@ -1843,11 +2020,11 @@ static int __init dquot_init(void)
 
        register_sysctl_table(sys_table);
 
-       dquot_cachep = kmem_cache_create("dquot", 
+       dquot_cachep = kmem_cache_create("dquot",
                        sizeof(struct dquot), sizeof(unsigned long) * 4,
                        (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
                                SLAB_MEM_SPREAD|SLAB_PANIC),
-                       NULL, NULL);
+                       NULL);
 
        order = 0;
        dquot_hash = (struct hlist_head *)__get_free_pages(GFP_ATOMIC, order);
@@ -1870,7 +2047,12 @@ static int __init dquot_init(void)
        printk("Dquot-cache hash table entries: %ld (order %ld, %ld bytes)\n",
                        nr_hash, order, (PAGE_SIZE << order));
 
-       set_shrinker(DEFAULT_SEEKS, shrink_dqcache_memory);
+       register_shrinker(&dqcache_shrinker);
+
+#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
+       if (genl_register_family(&quota_genl_family) != 0)
+               printk(KERN_ERR "VFS: Failed to create quota netlink interface.\n");
+#endif
 
        return 0;
 }