fs/affs/file.c: use BUG_ON
[safe/jmp/linux-2.6] / fs / dquot.c
index 2809768..dfba162 100644 (file)
@@ -289,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)
 {
@@ -696,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))
@@ -711,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) */
@@ -827,6 +840,18 @@ 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;
 
@@ -845,16 +870,12 @@ static inline int need_print_warning(struct dquot *dquot)
 }
 
 /* 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 == QUOTA_NL_BHARDWARN ||
-               warntype == QUOTA_NL_BSOFTLONGWARN) ? DQ_BLKS_B :
-               ((warntype == QUOTA_NL_IHARDWARN ||
-               warntype == QUOTA_NL_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);
@@ -895,9 +916,6 @@ out_lock:
 
 #ifdef CONFIG_QUOTA_NETLINK_INTERFACE
 
-/* Size of quota netlink message - actually an upperbound for buffer size */
-#define QUOTA_NL_MSG_SIZE 32
-
 /* Netlink family structure for quota */
 static struct genl_family quota_genl_family = {
        .id = GENL_ID_GENERATE,
@@ -914,11 +932,13 @@ static void send_warning(const struct dquot *dquot, const char warntype)
        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(QUOTA_NL_MSG_SIZE, GFP_NOFS);
+       skb = genlmsg_new(msg_size, GFP_NOFS);
        if (!skb) {
                printk(KERN_ERR
                  "VFS: Not enough memory to send quota warning.\n");
@@ -959,18 +979,19 @@ static void send_warning(const struct dquot *dquot, const char warntype)
                        "VFS: Failed to send notification message: %d\n", ret);
        return;
 attr_err_out:
-       printk(KERN_ERR "VFS: Failed to compose quota message: %d\n", ret);
+       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 **dquots, char *warntype)
+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] != QUOTA_NL_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
@@ -1216,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;
 }
@@ -1428,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];
 
@@ -1462,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);
@@ -1480,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;
@@ -1509,13 +1551,20 @@ int vfs_quota_off(struct super_block *sb, int type)
                                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;
 }
 
 /*
@@ -1553,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;
        }
@@ -1576,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) {
@@ -1611,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;
 }
 
@@ -1695,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) {
@@ -1730,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) {
@@ -1738,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);
@@ -1746,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 */