quota_v2: Implement get_next_id() for V2 quota format
authorJan Kara <jack@suse.cz>
Mon, 25 Jan 2016 19:39:27 +0000 (20:39 +0100)
committerJan Kara <jack@suse.cz>
Tue, 9 Feb 2016 12:05:23 +0000 (13:05 +0100)
Implement functions to get id of next existing quota structure in quota
file for quota tree based formats and thus for V2 quota format.

Signed-off-by: Jan Kara <jack@suse.cz>
fs/quota/quota_tree.c
fs/quota/quota_v2.c
include/linux/dqblk_qtree.h

index 58efb83..0738972 100644 (file)
@@ -22,10 +22,9 @@ MODULE_LICENSE("GPL");
 
 #define __QUOTA_QT_PARANOIA
 
-static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth)
+static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
 {
        unsigned int epb = info->dqi_usable_bs >> 2;
-       qid_t id = from_kqid(&init_user_ns, qid);
 
        depth = info->dqi_qtree_depth - depth - 1;
        while (depth--)
@@ -33,6 +32,13 @@ static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth)
        return id % epb;
 }
 
+static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth)
+{
+       qid_t id = from_kqid(&init_user_ns, qid);
+
+       return __get_index(info, id, depth);
+}
+
 /* Number of entries in one blocks */
 static int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
 {
@@ -668,3 +674,60 @@ int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
        return 0;
 }
 EXPORT_SYMBOL(qtree_release_dquot);
+
+static int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id,
+                       unsigned int blk, int depth)
+{
+       char *buf = getdqbuf(info->dqi_usable_bs);
+       __le32 *ref = (__le32 *)buf;
+       ssize_t ret;
+       unsigned int epb = info->dqi_usable_bs >> 2;
+       unsigned int level_inc = 1;
+       int i;
+
+       if (!buf)
+               return -ENOMEM;
+
+       for (i = depth; i < info->dqi_qtree_depth - 1; i++)
+               level_inc *= epb;
+
+       ret = read_blk(info, blk, buf);
+       if (ret < 0) {
+               quota_error(info->dqi_sb,
+                           "Can't read quota tree block %u", blk);
+               goto out_buf;
+       }
+       for (i = __get_index(info, *id, depth); i < epb; i++) {
+               if (ref[i] == cpu_to_le32(0)) {
+                       *id += level_inc;
+                       continue;
+               }
+               if (depth == info->dqi_qtree_depth - 1) {
+                       ret = 0;
+                       goto out_buf;
+               }
+               ret = find_next_id(info, id, le32_to_cpu(ref[i]), depth + 1);
+               if (ret != -ENOENT)
+                       break;
+       }
+       if (i == epb) {
+               ret = -ENOENT;
+               goto out_buf;
+       }
+out_buf:
+       kfree(buf);
+       return ret;
+}
+
+int qtree_get_next_id(struct qtree_mem_dqinfo *info, struct kqid *qid)
+{
+       qid_t id = from_kqid(&init_user_ns, *qid);
+       int ret;
+
+       ret = find_next_id(info, &id, QT_TREEOFF, 0);
+       if (ret < 0)
+               return ret;
+       *qid = make_kqid(&init_user_ns, qid->type, id);
+       return 0;
+}
+EXPORT_SYMBOL(qtree_get_next_id);
index ed85d4f..ca71bf8 100644 (file)
@@ -304,6 +304,11 @@ static int v2_free_file_info(struct super_block *sb, int type)
        return 0;
 }
 
+static int v2_get_next_id(struct super_block *sb, struct kqid *qid)
+{
+       return qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
+}
+
 static const struct quota_format_ops v2_format_ops = {
        .check_quota_file       = v2_check_quota_file,
        .read_file_info         = v2_read_file_info,
@@ -312,6 +317,7 @@ static const struct quota_format_ops v2_format_ops = {
        .read_dqblk             = v2_read_dquot,
        .commit_dqblk           = v2_write_dquot,
        .release_dqblk          = v2_release_dquot,
+       .get_next_id            = v2_get_next_id,
 };
 
 static struct quota_format_type v2r0_quota_format = {
index ff8b553..0de21e9 100644 (file)
@@ -15,6 +15,7 @@
 #define QTREE_DEL_REWRITE 6
 
 struct dquot;
+struct kqid;
 
 /* Operations */
 struct qtree_fmt_operations {
@@ -52,5 +53,6 @@ static inline int qtree_depth(struct qtree_mem_dqinfo *info)
                entries *= epb;
        return i;
 }
+int qtree_get_next_id(struct qtree_mem_dqinfo *info, struct kqid *qid);
 
 #endif /* _LINUX_DQBLK_QTREE_H */