GFS2: Remove dirent_first() function
[safe/jmp/linux-2.6] / fs / gfs2 / quota.c
index f790f5a..e3bf6ea 100644 (file)
@@ -15,7 +15,7 @@
  * fuzziness in the current usage value of IDs that are being used on different
  * nodes in the cluster simultaneously.  So, it is possible for a user on
  * multiple nodes to overrun their quota, but that overrun is controlable.
- * Since quota tags are part of transactions, there is no need to a quota check
+ * Since quota tags are part of transactions, there is no need for a quota check
  * program to be run on node crashes or anything like that.
  *
  * There are couple of knobs that let the administrator manage the quota
@@ -47,6 +47,7 @@
 #include <linux/gfs2_ondisk.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
+#include <linux/quota.h>
 #include <linux/dqblk_xfs.h>
 
 #include "gfs2.h"
 #define QUOTA_USER 1
 #define QUOTA_GROUP 0
 
-struct gfs2_quota_host {
-       u64 qu_limit;
-       u64 qu_warn;
-       s64 qu_value;
-       u32 qu_ll_next;
-};
-
 struct gfs2_quota_change_host {
        u64 qc_change;
        u32 qc_flags; /* GFS2_QCF_... */
@@ -165,7 +159,7 @@ fail:
        return error;
 }
 
-static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, int create,
+static int qd_get(struct gfs2_sbd *sdp, int user, u32 id,
                  struct gfs2_quota_data **qdp)
 {
        struct gfs2_quota_data *qd = NULL, *new_qd = NULL;
@@ -203,7 +197,7 @@ static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, int create,
 
                spin_unlock(&qd_lru_lock);
 
-               if (qd || !create) {
+               if (qd) {
                        if (new_qd) {
                                gfs2_glock_put(new_qd->qd_gl);
                                kmem_cache_free(gfs2_quotad_cachep, new_qd);
@@ -467,7 +461,7 @@ static int qdsb_get(struct gfs2_sbd *sdp, int user, u32 id,
 {
        int error;
 
-       error = qd_get(sdp, user, id, CREATE, qdp);
+       error = qd_get(sdp, user, id, qdp);
        if (error)
                return error;
 
@@ -618,48 +612,36 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change)
        mutex_unlock(&sdp->sd_quota_mutex);
 }
 
-static void gfs2_quota_in(struct gfs2_quota_host *qu, const void *buf)
-{
-       const struct gfs2_quota *str = buf;
-
-       qu->qu_limit = be64_to_cpu(str->qu_limit);
-       qu->qu_warn = be64_to_cpu(str->qu_warn);
-       qu->qu_value = be64_to_cpu(str->qu_value);
-       qu->qu_ll_next = be32_to_cpu(str->qu_ll_next);
-}
-
-static void gfs2_quota_out(const struct gfs2_quota_host *qu, void *buf)
-{
-       struct gfs2_quota *str = buf;
-
-       str->qu_limit = cpu_to_be64(qu->qu_limit);
-       str->qu_warn = cpu_to_be64(qu->qu_warn);
-       str->qu_value = cpu_to_be64(qu->qu_value);
-       str->qu_ll_next = cpu_to_be32(qu->qu_ll_next);
-       memset(&str->qu_reserved, 0, sizeof(str->qu_reserved));
-}
-
 /**
- * gfs2_adjust_quota
+ * gfs2_adjust_quota - adjust record of current block usage
+ * @ip: The quota inode
+ * @loc: Offset of the entry in the quota file
+ * @change: The amount of usage change to record
+ * @qd: The quota data
+ * @fdq: The updated limits to record
  *
  * This function was mostly borrowed from gfs2_block_truncate_page which was
  * in turn mostly borrowed from ext3
+ *
+ * Returns: 0 or -ve on error
  */
+
 static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
-                            s64 change, struct gfs2_quota_data *qd)
+                            s64 change, struct gfs2_quota_data *qd,
+                            struct fs_disk_quota *fdq)
 {
        struct inode *inode = &ip->i_inode;
        struct address_space *mapping = inode->i_mapping;
        unsigned long index = loc >> PAGE_CACHE_SHIFT;
        unsigned offset = loc & (PAGE_CACHE_SIZE - 1);
        unsigned blocksize, iblock, pos;
-       struct buffer_head *bh;
+       struct buffer_head *bh, *dibh;
        struct page *page;
        void *kaddr;
-       char *ptr;
-       struct gfs2_quota_host qp;
+       struct gfs2_quota *qp;
        s64 value;
        int err = -EIO;
+       u64 size;
 
        if (gfs2_is_stuffed(ip))
                gfs2_unstuff_dinode(ip, NULL);
@@ -701,18 +683,38 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
        gfs2_trans_add_bh(ip->i_gl, bh, 0);
 
        kaddr = kmap_atomic(page, KM_USER0);
-       ptr = kaddr + offset;
-       gfs2_quota_in(&qp, ptr);
-       qp.qu_value += change;
-       value = qp.qu_value;
-       gfs2_quota_out(&qp, ptr);
+       qp = kaddr + offset;
+       value = (s64)be64_to_cpu(qp->qu_value) + change;
+       qp->qu_value = cpu_to_be64(value);
+       qd->qd_qb.qb_value = qp->qu_value;
+       if (fdq) {
+               if (fdq->d_fieldmask & FS_DQ_BSOFT) {
+                       qp->qu_warn = cpu_to_be64(fdq->d_blk_softlimit);
+                       qd->qd_qb.qb_warn = qp->qu_warn;
+               }
+               if (fdq->d_fieldmask & FS_DQ_BHARD) {
+                       qp->qu_limit = cpu_to_be64(fdq->d_blk_hardlimit);
+                       qd->qd_qb.qb_limit = qp->qu_limit;
+               }
+       }
        flush_dcache_page(page);
        kunmap_atomic(kaddr, KM_USER0);
-       err = 0;
-       qd->qd_qb.qb_magic = cpu_to_be32(GFS2_MAGIC);
-       qd->qd_qb.qb_value = cpu_to_be64(value);
-       ((struct gfs2_quota_lvb*)(qd->qd_gl->gl_lvb))->qb_magic = cpu_to_be32(GFS2_MAGIC);
-       ((struct gfs2_quota_lvb*)(qd->qd_gl->gl_lvb))->qb_value = cpu_to_be64(value);
+
+       err = gfs2_meta_inode_buffer(ip, &dibh);
+       if (err)
+               goto unlock;
+
+       size = loc + sizeof(struct gfs2_quota);
+       if (size > inode->i_size) {
+               ip->i_disksize = size;
+               i_size_write(inode, size);
+       }
+       inode->i_mtime = inode->i_atime = CURRENT_TIME;
+       gfs2_trans_add_bh(ip->i_gl, dibh, 1);
+       gfs2_dinode_out(ip, dibh->b_data);
+       brelse(dibh);
+       mark_inode_dirty(inode);
+
 unlock:
        unlock_page(page);
        page_cache_release(page);
@@ -740,9 +742,9 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
                return -ENOMEM;
 
        sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL);
+       mutex_lock_nested(&ip->i_inode.i_mutex, I_MUTEX_QUOTA);
        for (qx = 0; qx < num_qd; qx++) {
-               error = gfs2_glock_nq_init(qda[qx]->qd_gl,
-                                          LM_ST_EXCLUSIVE,
+               error = gfs2_glock_nq_init(qda[qx]->qd_gl, LM_ST_EXCLUSIVE,
                                           GL_NOCACHE, &ghs[qx]);
                if (error)
                        goto out;
@@ -796,9 +798,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
        for (x = 0; x < num_qd; x++) {
                qd = qda[x];
                offset = qd2offset(qd);
-               error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync,
-                                         (struct gfs2_quota_data *)
-                                         qd);
+               error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync, qd, NULL);
                if (error)
                        goto out_end_trans;
 
@@ -818,21 +818,44 @@ out_gunlock:
 out:
        while (qx--)
                gfs2_glock_dq_uninit(&ghs[qx]);
+       mutex_unlock(&ip->i_inode.i_mutex);
        kfree(ghs);
        gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl);
        return error;
 }
 
+static int update_qd(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd)
+{
+       struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
+       struct gfs2_quota q;
+       struct gfs2_quota_lvb *qlvb;
+       loff_t pos;
+       int error;
+
+       memset(&q, 0, sizeof(struct gfs2_quota));
+       pos = qd2offset(qd);
+       error = gfs2_internal_read(ip, NULL, (char *)&q, &pos, sizeof(q));
+       if (error < 0)
+               return error;
+
+       qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
+       qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC);
+       qlvb->__pad = 0;
+       qlvb->qb_limit = q.qu_limit;
+       qlvb->qb_warn = q.qu_warn;
+       qlvb->qb_value = q.qu_value;
+       qd->qd_qb = *qlvb;
+
+       return 0;
+}
+
 static int do_glock(struct gfs2_quota_data *qd, int force_refresh,
                    struct gfs2_holder *q_gh)
 {
        struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
        struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
        struct gfs2_holder i_gh;
-       struct gfs2_quota_host q;
-       char buf[sizeof(struct gfs2_quota)];
        int error;
-       struct gfs2_quota_lvb *qlvb;
 
 restart:
        error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh);
@@ -842,7 +865,6 @@ restart:
        qd->qd_qb = *(struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
 
        if (force_refresh || qd->qd_qb.qb_magic != cpu_to_be32(GFS2_MAGIC)) {
-               loff_t pos;
                gfs2_glock_dq_uninit(q_gh);
                error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_EXCLUSIVE,
                                           GL_NOCACHE, q_gh);
@@ -853,24 +875,11 @@ restart:
                if (error)
                        goto fail;
 
-               memset(buf, 0, sizeof(struct gfs2_quota));
-               pos = qd2offset(qd);
-               error = gfs2_internal_read(ip, NULL, buf, &pos,
-                                          sizeof(struct gfs2_quota));
-               if (error < 0)
+               error = update_qd(sdp, qd);
+               if (error)
                        goto fail_gunlock;
 
                gfs2_glock_dq_uninit(&i_gh);
-
-               gfs2_quota_in(&q, buf);
-               qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
-               qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC);
-               qlvb->__pad = 0;
-               qlvb->qb_limit = cpu_to_be64(q.qu_limit);
-               qlvb->qb_warn = cpu_to_be64(q.qu_warn);
-               qlvb->qb_value = cpu_to_be64(q.qu_value);
-               qd->qd_qb = *qlvb;
-
                gfs2_glock_dq_uninit(q_gh);
                force_refresh = 0;
                goto restart;
@@ -993,7 +1002,7 @@ static int print_message(struct gfs2_quota_data *qd, char *type)
 {
        struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
 
-       printk(KERN_INFO "GFS2: fsid=%s: quota %s for %s %u\r\n",
+       printk(KERN_INFO "GFS2: fsid=%s: quota %s for %s %u\n",
               sdp->sd_fsname, type,
               (test_bit(QDF_USER, &qd->qd_flags)) ? "user" : "group",
               qd->qd_id);
@@ -1030,6 +1039,10 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
 
                if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
                        print_message(qd, "exceeded");
+                       quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
+                                          USRQUOTA : GRPQUOTA, qd->qd_id,
+                                          sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
+
                        error = -EDQUOT;
                        break;
                } else if (be64_to_cpu(qd->qd_qb.qb_warn) &&
@@ -1037,6 +1050,9 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
                           time_after_eq(jiffies, qd->qd_last_warn +
                                         gfs2_tune_get(sdp,
                                                gt_quota_warn_period) * HZ)) {
+                       quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
+                                          USRQUOTA : GRPQUOTA, qd->qd_id,
+                                          sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN);
                        error = print_message(qd, "warning");
                        qd->qd_last_warn = jiffies;
                }
@@ -1117,7 +1133,7 @@ int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id)
        struct gfs2_holder q_gh;
        int error;
 
-       error = qd_get(sdp, user, id, CREATE, &qd);
+       error = qd_get(sdp, user, id, &qd);
        if (error)
                return error;
 
@@ -1126,7 +1142,6 @@ int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id)
                gfs2_glock_dq_uninit(&q_gh);
 
        qd_put(qd);
-
        return error;
 }
 
@@ -1329,6 +1344,14 @@ static void quotad_check_trunc_list(struct gfs2_sbd *sdp)
        }
 }
 
+void gfs2_wake_up_statfs(struct gfs2_sbd *sdp) {
+       if (!sdp->sd_statfs_force_sync) {
+               sdp->sd_statfs_force_sync = 1;
+               wake_up(&sdp->sd_quota_wait);
+       }
+}
+
+
 /**
  * gfs2_quotad - Write cached quota changes into the quota file
  * @sdp: Pointer to GFS2 superblock
@@ -1348,8 +1371,15 @@ int gfs2_quotad(void *data)
        while (!kthread_should_stop()) {
 
                /* Update the master statfs file */
-               quotad_check_timeo(sdp, "statfs", gfs2_statfs_sync, t,
-                                  &statfs_timeo, &tune->gt_statfs_quantum);
+               if (sdp->sd_statfs_force_sync) {
+                       int error = gfs2_statfs_sync(sdp->sd_vfs, 0);
+                       quotad_error(sdp, "statfs", error);
+                       statfs_timeo = gfs2_tune_get(sdp, gt_statfs_quantum) * HZ;
+               }
+               else
+                       quotad_check_timeo(sdp, "statfs", gfs2_statfs_sync, t,
+                                          &statfs_timeo,
+                                          &tune->gt_statfs_quantum);
 
                /* Update quota file */
                quotad_check_timeo(sdp, "sync", gfs2_quota_sync, t,
@@ -1366,7 +1396,7 @@ int gfs2_quotad(void *data)
                spin_lock(&sdp->sd_trunc_lock);
                empty = list_empty(&sdp->sd_trunc_list);
                spin_unlock(&sdp->sd_trunc_lock);
-               if (empty)
+               if (empty && !sdp->sd_statfs_force_sync)
                        t -= schedule_timeout(t);
                else
                        t = 0;
@@ -1397,8 +1427,160 @@ static int gfs2_quota_get_xstate(struct super_block *sb,
        return 0;
 }
 
+static int gfs2_xquota_get(struct super_block *sb, int type, qid_t id,
+                          struct fs_disk_quota *fdq)
+{
+       struct gfs2_sbd *sdp = sb->s_fs_info;
+       struct gfs2_quota_lvb *qlvb;
+       struct gfs2_quota_data *qd;
+       struct gfs2_holder q_gh;
+       int error;
+
+       memset(fdq, 0, sizeof(struct fs_disk_quota));
+
+       if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
+               return -ESRCH; /* Crazy XFS error code */
+
+       if (type == USRQUOTA)
+               type = QUOTA_USER;
+       else if (type == GRPQUOTA)
+               type = QUOTA_GROUP;
+       else
+               return -EINVAL;
+
+       error = qd_get(sdp, type, id, &qd);
+       if (error)
+               return error;
+       error = do_glock(qd, FORCE, &q_gh);
+       if (error)
+               goto out;
+
+       qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
+       fdq->d_version = FS_DQUOT_VERSION;
+       fdq->d_flags = (type == QUOTA_USER) ? XFS_USER_QUOTA : XFS_GROUP_QUOTA;
+       fdq->d_id = id;
+       fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit);
+       fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn);
+       fdq->d_bcount = be64_to_cpu(qlvb->qb_value);
+
+       gfs2_glock_dq_uninit(&q_gh);
+out:
+       qd_put(qd);
+       return error;
+}
+
+/* GFS2 only supports a subset of the XFS fields */
+#define GFS2_FIELDMASK (FS_DQ_BSOFT|FS_DQ_BHARD)
+
+static int gfs2_xquota_set(struct super_block *sb, int type, qid_t id,
+                          struct fs_disk_quota *fdq)
+{
+       struct gfs2_sbd *sdp = sb->s_fs_info;
+       struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
+       struct gfs2_quota_data *qd;
+       struct gfs2_holder q_gh, i_gh;
+       unsigned int data_blocks, ind_blocks;
+       unsigned int blocks = 0;
+       int alloc_required;
+       struct gfs2_alloc *al;
+       loff_t offset;
+       int error;
+
+       if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
+               return -ESRCH; /* Crazy XFS error code */
+
+       switch(type) {
+       case USRQUOTA:
+               type = QUOTA_USER;
+               if (fdq->d_flags != XFS_USER_QUOTA)
+                       return -EINVAL;
+               break;
+       case GRPQUOTA:
+               type = QUOTA_GROUP;
+               if (fdq->d_flags != XFS_GROUP_QUOTA)
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (fdq->d_fieldmask & ~GFS2_FIELDMASK)
+               return -EINVAL;
+       if (fdq->d_id != id)
+               return -EINVAL;
+
+       error = qd_get(sdp, type, id, &qd);
+       if (error)
+               return error;
+
+       mutex_lock(&ip->i_inode.i_mutex);
+       error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_EXCLUSIVE, 0, &q_gh);
+       if (error)
+               goto out_put;
+       error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+       if (error)
+               goto out_q;
+
+       /* Check for existing entry, if none then alloc new blocks */
+       error = update_qd(sdp, qd);
+       if (error)
+               goto out_i;
+
+       /* If nothing has changed, this is a no-op */
+       if ((fdq->d_fieldmask & FS_DQ_BSOFT) &&
+           (fdq->d_blk_softlimit == be64_to_cpu(qd->qd_qb.qb_warn)))
+               fdq->d_fieldmask ^= FS_DQ_BSOFT;
+       if ((fdq->d_fieldmask & FS_DQ_BHARD) &&
+           (fdq->d_blk_hardlimit == be64_to_cpu(qd->qd_qb.qb_limit)))
+               fdq->d_fieldmask ^= FS_DQ_BHARD;
+       if (fdq->d_fieldmask == 0)
+               goto out_i;
+
+       offset = qd2offset(qd);
+       error = gfs2_write_alloc_required(ip, offset, sizeof(struct gfs2_quota),
+                                         &alloc_required);
+       if (error)
+               goto out_i;
+       if (alloc_required) {
+               al = gfs2_alloc_get(ip);
+               if (al == NULL)
+                       goto out_i;
+               gfs2_write_calc_reserv(ip, sizeof(struct gfs2_quota),
+                                      &data_blocks, &ind_blocks);
+               blocks = al->al_requested = 1 + data_blocks + ind_blocks;
+               error = gfs2_inplace_reserve(ip);
+               if (error)
+                       goto out_alloc;
+       }
+
+       error = gfs2_trans_begin(sdp, blocks + RES_DINODE + 1, 0);
+       if (error)
+               goto out_release;
+
+       /* Apply changes */
+       error = gfs2_adjust_quota(ip, offset, 0, qd, fdq);
+
+       gfs2_trans_end(sdp);
+out_release:
+       if (alloc_required) {
+               gfs2_inplace_release(ip);
+out_alloc:
+               gfs2_alloc_put(ip);
+       }
+out_i:
+       gfs2_glock_dq_uninit(&i_gh);
+out_q:
+       gfs2_glock_dq_uninit(&q_gh);
+out_put:
+       mutex_unlock(&ip->i_inode.i_mutex);
+       qd_put(qd);
+       return error;
+}
+
 const struct quotactl_ops gfs2_quotactl_ops = {
        .quota_sync     = gfs2_quota_sync,
        .get_xstate     = gfs2_quota_get_xstate,
+       .get_xquota     = gfs2_xquota_get,
+       .set_xquota     = gfs2_xquota_set,
 };