quota: add new quotactl Q_GETNEXTQUOTA
[cascardo/linux.git] / fs / quota / quota.c
index 3746367..0ebc904 100644 (file)
@@ -79,7 +79,7 @@ unsigned int qtype_enforce_flag(int type)
        return 0;
 }
 
-static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
+static int quota_quotaon(struct super_block *sb, int type, qid_t id,
                         struct path *path)
 {
        if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_enable)
@@ -222,6 +222,34 @@ static int quota_getquota(struct super_block *sb, int type, qid_t id,
        return 0;
 }
 
+/*
+ * Return quota for next active quota >= this id, if any exists,
+ * otherwise return -ESRCH via ->get_nextdqblk
+ */
+static int quota_getnextquota(struct super_block *sb, int type, qid_t id,
+                         void __user *addr)
+{
+       struct kqid qid;
+       struct qc_dqblk fdq;
+       struct if_nextdqblk idq;
+       int ret;
+
+       if (!sb->s_qcop->get_nextdqblk)
+               return -ENOSYS;
+       qid = make_kqid(current_user_ns(), type, id);
+       if (!qid_valid(qid))
+               return -EINVAL;
+       ret = sb->s_qcop->get_nextdqblk(sb, &qid, &fdq);
+       if (ret)
+               return ret;
+       /* struct if_nextdqblk is a superset of struct if_dqblk */
+       copy_to_if_dqblk((struct if_dqblk *)&idq, &fdq);
+       idq.dqb_id = from_kqid(current_user_ns(), qid);
+       if (copy_to_user(addr, &idq, sizeof(idq)))
+               return -EFAULT;
+       return 0;
+}
+
 static void copy_from_if_dqblk(struct qc_dqblk *dst, struct if_dqblk *src)
 {
        dst->d_spc_hardlimit = qbtos(src->dqb_bhardlimit);
@@ -625,6 +653,34 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id,
        return ret;
 }
 
+/*
+ * Return quota for next active quota >= this id, if any exists,
+ * otherwise return -ESRCH via ->get_nextdqblk.
+ */
+static int quota_getnextxquota(struct super_block *sb, int type, qid_t id,
+                           void __user *addr)
+{
+       struct fs_disk_quota fdq;
+       struct qc_dqblk qdq;
+       struct kqid qid;
+       qid_t id_out;
+       int ret;
+
+       if (!sb->s_qcop->get_nextdqblk)
+               return -ENOSYS;
+       qid = make_kqid(current_user_ns(), type, id);
+       if (!qid_valid(qid))
+               return -EINVAL;
+       ret = sb->s_qcop->get_nextdqblk(sb, &qid, &qdq);
+       if (ret)
+               return ret;
+       id_out = from_kqid(current_user_ns(), qid);
+       copy_to_xfs_dqblk(&fdq, &qdq, type, id_out);
+       if (copy_to_user(addr, &fdq, sizeof(fdq)))
+               return -EFAULT;
+       return ret;
+}
+
 static int quota_rmxquota(struct super_block *sb, void __user *addr)
 {
        __u32 flags;
@@ -659,7 +715,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
 
        switch (cmd) {
        case Q_QUOTAON:
-               return quota_quotaon(sb, type, cmd, id, path);
+               return quota_quotaon(sb, type, id, path);
        case Q_QUOTAOFF:
                return quota_quotaoff(sb, type);
        case Q_GETFMT:
@@ -670,6 +726,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
                return quota_setinfo(sb, type, addr);
        case Q_GETQUOTA:
                return quota_getquota(sb, type, id, addr);
+       case Q_GETNEXTQUOTA:
+               return quota_getnextquota(sb, type, id, addr);
        case Q_SETQUOTA:
                return quota_setquota(sb, type, id, addr);
        case Q_SYNC:
@@ -690,6 +748,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
                return quota_setxquota(sb, type, id, addr);
        case Q_XGETQUOTA:
                return quota_getxquota(sb, type, id, addr);
+       case Q_XGETNEXTQUOTA:
+               return quota_getnextxquota(sb, type, id, addr);
        case Q_XQUOTASYNC:
                if (sb->s_flags & MS_RDONLY)
                        return -EROFS;
@@ -708,10 +768,12 @@ static int quotactl_cmd_write(int cmd)
        switch (cmd) {
        case Q_GETFMT:
        case Q_GETINFO:
+       case Q_GETNEXTQUOTA:
        case Q_SYNC:
        case Q_XGETQSTAT:
        case Q_XGETQSTATV:
        case Q_XGETQUOTA:
+       case Q_XGETNEXTQUOTA:
        case Q_XQUOTASYNC:
                return 0;
        }