root/fs/quota/quota_tree.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 *      vfsv0 quota IO operations on file
 */

#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/dqblk_v2.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/quotaops.h>

#include <asm/byteorder.h>

#include "quota_tree.h"

MODULE_AUTHOR("Jan Kara");
MODULE_DESCRIPTION("Quota trie support");
MODULE_LICENSE("GPL");

/*
 * Maximum quota tree depth we support. Only to limit recursion when working
 * with the tree.
 */
#define MAX_QTREE_DEPTH 6

#define __QUOTA_QT_PARANOIA

static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
{
        unsigned int epb = info->dqi_usable_bs >> 2;

        depth = info->dqi_qtree_depth - depth - 1;
        while (depth--)
                id /= epb;
        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)
{
        return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader))
               / info->dqi_entry_size;
}

static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
{
        struct super_block *sb = info->dqi_sb;

        memset(buf, 0, info->dqi_usable_bs);
        return sb->s_op->quota_read(sb, info->dqi_type, buf,
               info->dqi_usable_bs, (loff_t)blk << info->dqi_blocksize_bits);
}

static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
{
        struct super_block *sb = info->dqi_sb;
        ssize_t ret;

        ret = sb->s_op->quota_write(sb, info->dqi_type, buf,
               info->dqi_usable_bs, (loff_t)blk << info->dqi_blocksize_bits);
        if (ret != info->dqi_usable_bs) {
                quota_error(sb, "dquota write failed");
                if (ret >= 0)
                        ret = -EIO;
        }
        return ret;
}

static inline int do_check_range(struct super_block *sb, const char *val_name,
                                 uint val, uint min_val, uint max_val)
{
        if (val < min_val || val > max_val) {
                quota_error(sb, "Getting %s %u out of range %u-%u",
                            val_name, val, min_val, max_val);
                return -EUCLEAN;
        }

        return 0;
}

static int check_dquot_block_header(struct qtree_mem_dqinfo *info,
                                    struct qt_disk_dqdbheader *dh)
{
        int err = 0;

        err = do_check_range(info->dqi_sb, "dqdh_next_free",
                             le32_to_cpu(dh->dqdh_next_free), 0,
                             info->dqi_blocks - 1);
        if (err)
                return err;
        err = do_check_range(info->dqi_sb, "dqdh_prev_free",
                             le32_to_cpu(dh->dqdh_prev_free), 0,
                             info->dqi_blocks - 1);
        if (err)
                return err;
        err = do_check_range(info->dqi_sb, "dqdh_entries",
                             le16_to_cpu(dh->dqdh_entries), 0,
                             qtree_dqstr_in_blk(info));

        return err;
}

/* Remove empty block from list and return it */
static int get_free_dqblk(struct qtree_mem_dqinfo *info)
{
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
        int ret, blk;

        if (!buf)
                return -ENOMEM;
        if (info->dqi_free_blk) {
                blk = info->dqi_free_blk;
                ret = read_blk(info, blk, buf);
                if (ret < 0)
                        goto out_buf;
                ret = check_dquot_block_header(info, dh);
                if (ret)
                        goto out_buf;
                info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
        }
        else {
                memset(buf, 0, info->dqi_usable_bs);
                /* Assure block allocation... */
                ret = write_blk(info, info->dqi_blocks, buf);
                if (ret < 0)
                        goto out_buf;
                blk = info->dqi_blocks++;
        }
        mark_info_dirty(info->dqi_sb, info->dqi_type);
        ret = blk;
out_buf:
        kfree(buf);
        return ret;
}

/* Insert empty block to the list */
static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk)
{
        struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
        int err;

        dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
        dh->dqdh_prev_free = cpu_to_le32(0);
        dh->dqdh_entries = cpu_to_le16(0);
        err = write_blk(info, blk, buf);
        if (err < 0)
                return err;
        info->dqi_free_blk = blk;
        mark_info_dirty(info->dqi_sb, info->dqi_type);
        return 0;
}

/* Remove given block from the list of blocks with free entries */
static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
                               uint blk)
{
        char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
        uint nextblk = le32_to_cpu(dh->dqdh_next_free);
        uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
        int err;

        if (!tmpbuf)
                return -ENOMEM;
        if (nextblk) {
                err = read_blk(info, nextblk, tmpbuf);
                if (err < 0)
                        goto out_buf;
                ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
                                                        dh->dqdh_prev_free;
                err = write_blk(info, nextblk, tmpbuf);
                if (err < 0)
                        goto out_buf;
        }
        if (prevblk) {
                err = read_blk(info, prevblk, tmpbuf);
                if (err < 0)
                        goto out_buf;
                ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
                                                        dh->dqdh_next_free;
                err = write_blk(info, prevblk, tmpbuf);
                if (err < 0)
                        goto out_buf;
        } else {
                info->dqi_free_entry = nextblk;
                mark_info_dirty(info->dqi_sb, info->dqi_type);
        }
        kfree(tmpbuf);
        dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
        /* No matter whether write succeeds block is out of list */
        if (write_blk(info, blk, buf) < 0)
                quota_error(info->dqi_sb, "Can't write block (%u) "
                            "with free entries", blk);
        return 0;
out_buf:
        kfree(tmpbuf);
        return err;
}

/* Insert given block to the beginning of list with free entries */
static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
                               uint blk)
{
        char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
        int err;

        if (!tmpbuf)
                return -ENOMEM;
        dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry);
        dh->dqdh_prev_free = cpu_to_le32(0);
        err = write_blk(info, blk, buf);
        if (err < 0)
                goto out_buf;
        if (info->dqi_free_entry) {
                err = read_blk(info, info->dqi_free_entry, tmpbuf);
                if (err < 0)
                        goto out_buf;
                ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
                                                        cpu_to_le32(blk);
                err = write_blk(info, info->dqi_free_entry, tmpbuf);
                if (err < 0)
                        goto out_buf;
        }
        kfree(tmpbuf);
        info->dqi_free_entry = blk;
        mark_info_dirty(info->dqi_sb, info->dqi_type);
        return 0;
out_buf:
        kfree(tmpbuf);
        return err;
}

/* Is the entry in the block free? */
int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
{
        int i;

        for (i = 0; i < info->dqi_entry_size; i++)
                if (disk[i])
                        return 0;
        return 1;
}
EXPORT_SYMBOL(qtree_entry_unused);

/* Find space for dquot */
static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
                              struct dquot *dquot, int *err)
{
        uint blk, i;
        struct qt_disk_dqdbheader *dh;
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        char *ddquot;

        *err = 0;
        if (!buf) {
                *err = -ENOMEM;
                return 0;
        }
        dh = (struct qt_disk_dqdbheader *)buf;
        if (info->dqi_free_entry) {
                blk = info->dqi_free_entry;
                *err = read_blk(info, blk, buf);
                if (*err < 0)
                        goto out_buf;
                *err = check_dquot_block_header(info, dh);
                if (*err)
                        goto out_buf;
        } else {
                blk = get_free_dqblk(info);
                if ((int)blk < 0) {
                        *err = blk;
                        kfree(buf);
                        return 0;
                }
                memset(buf, 0, info->dqi_usable_bs);
                /* This is enough as the block is already zeroed and the entry
                 * list is empty... */
                info->dqi_free_entry = blk;
                mark_info_dirty(dquot->dq_sb, dquot->dq_id.type);
        }
        /* Block will be full? */
        if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
                *err = remove_free_dqentry(info, buf, blk);
                if (*err < 0) {
                        quota_error(dquot->dq_sb, "Can't remove block (%u) "
                                    "from entry free list", blk);
                        goto out_buf;
                }
        }
        le16_add_cpu(&dh->dqdh_entries, 1);
        /* Find free structure in block */
        ddquot = buf + sizeof(struct qt_disk_dqdbheader);
        for (i = 0; i < qtree_dqstr_in_blk(info); i++) {
                if (qtree_entry_unused(info, ddquot))
                        break;
                ddquot += info->dqi_entry_size;
        }
#ifdef __QUOTA_QT_PARANOIA
        if (i == qtree_dqstr_in_blk(info)) {
                quota_error(dquot->dq_sb, "Data block full but it shouldn't");
                *err = -EIO;
                goto out_buf;
        }
#endif
        *err = write_blk(info, blk, buf);
        if (*err < 0) {
                quota_error(dquot->dq_sb, "Can't write quota data block %u",
                            blk);
                goto out_buf;
        }
        dquot->dq_off = ((loff_t)blk << info->dqi_blocksize_bits) +
                        sizeof(struct qt_disk_dqdbheader) +
                        i * info->dqi_entry_size;
        kfree(buf);
        return blk;
out_buf:
        kfree(buf);
        return 0;
}

/* Insert reference to structure into the trie */
static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
                          uint *blks, int depth)
{
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        int ret = 0, newson = 0, newact = 0;
        __le32 *ref;
        uint newblk;
        int i;

        if (!buf)
                return -ENOMEM;
        if (!blks[depth]) {
                ret = get_free_dqblk(info);
                if (ret < 0)
                        goto out_buf;
                for (i = 0; i < depth; i++)
                        if (ret == blks[i]) {
                                quota_error(dquot->dq_sb,
                                        "Free block already used in tree: block %u",
                                        ret);
                                ret = -EIO;
                                goto out_buf;
                        }
                blks[depth] = ret;
                memset(buf, 0, info->dqi_usable_bs);
                newact = 1;
        } else {
                ret = read_blk(info, blks[depth], buf);
                if (ret < 0) {
                        quota_error(dquot->dq_sb, "Can't read tree quota "
                                    "block %u", blks[depth]);
                        goto out_buf;
                }
        }
        ref = (__le32 *)buf;
        newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
        ret = do_check_range(dquot->dq_sb, "block", newblk, 0,
                             info->dqi_blocks - 1);
        if (ret)
                goto out_buf;
        if (!newblk) {
                newson = 1;
        } else {
                for (i = 0; i <= depth; i++)
                        if (newblk == blks[i]) {
                                quota_error(dquot->dq_sb,
                                        "Cycle in quota tree detected: block %u index %u",
                                        blks[depth],
                                        get_index(info, dquot->dq_id, depth));
                                ret = -EIO;
                                goto out_buf;
                        }
        }
        blks[depth + 1] = newblk;
        if (depth == info->dqi_qtree_depth - 1) {
#ifdef __QUOTA_QT_PARANOIA
                if (newblk) {
                        quota_error(dquot->dq_sb, "Inserting already present "
                                    "quota entry (block %u)",
                                    le32_to_cpu(ref[get_index(info,
                                                dquot->dq_id, depth)]));
                        ret = -EIO;
                        goto out_buf;
                }
#endif
                blks[depth + 1] = find_free_dqentry(info, dquot, &ret);
        } else {
                ret = do_insert_tree(info, dquot, blks, depth + 1);
        }
        if (newson && ret >= 0) {
                ref[get_index(info, dquot->dq_id, depth)] =
                                                cpu_to_le32(blks[depth + 1]);
                ret = write_blk(info, blks[depth], buf);
        } else if (newact && ret < 0) {
                put_free_dqblk(info, buf, blks[depth]);
        }
out_buf:
        kfree(buf);
        return ret;
}

/* Wrapper for inserting quota structure into tree */
static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
                                 struct dquot *dquot)
{
        uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };

#ifdef __QUOTA_QT_PARANOIA
        if (info->dqi_blocks <= QT_TREEOFF) {
                quota_error(dquot->dq_sb, "Quota tree root isn't allocated!");
                return -EIO;
        }
#endif
        if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
                quota_error(dquot->dq_sb, "Quota tree depth too big!");
                return -EIO;
        }
        return do_insert_tree(info, dquot, blks, 0);
}

/*
 * We don't have to be afraid of deadlocks as we never have quotas on quota
 * files...
 */
int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
        int type = dquot->dq_id.type;
        struct super_block *sb = dquot->dq_sb;
        ssize_t ret;
        char *ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);

        if (!ddquot)
                return -ENOMEM;

        /* dq_off is guarded by dqio_sem */
        if (!dquot->dq_off) {
                ret = dq_insert_tree(info, dquot);
                if (ret < 0) {
                        quota_error(sb, "Error %zd occurred while creating "
                                    "quota", ret);
                        kfree(ddquot);
                        return ret;
                }
        }
        spin_lock(&dquot->dq_dqb_lock);
        info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
        spin_unlock(&dquot->dq_dqb_lock);
        ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
                                    dquot->dq_off);
        if (ret != info->dqi_entry_size) {
                quota_error(sb, "dquota write failed");
                if (ret >= 0)
                        ret = -ENOSPC;
        } else {
                ret = 0;
        }
        dqstats_inc(DQST_WRITES);
        kfree(ddquot);

        return ret;
}
EXPORT_SYMBOL(qtree_write_dquot);

/* Free dquot entry in data block */
static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
                        uint blk)
{
        struct qt_disk_dqdbheader *dh;
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        int ret = 0;

        if (!buf)
                return -ENOMEM;
        if (dquot->dq_off >> info->dqi_blocksize_bits != blk) {
                quota_error(dquot->dq_sb, "Quota structure has offset to "
                        "other block (%u) than it should (%u)", blk,
                        (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
                ret = -EIO;
                goto out_buf;
        }
        ret = read_blk(info, blk, buf);
        if (ret < 0) {
                quota_error(dquot->dq_sb, "Can't read quota data block %u",
                            blk);
                goto out_buf;
        }
        dh = (struct qt_disk_dqdbheader *)buf;
        ret = check_dquot_block_header(info, dh);
        if (ret)
                goto out_buf;
        le16_add_cpu(&dh->dqdh_entries, -1);
        if (!le16_to_cpu(dh->dqdh_entries)) {   /* Block got free? */
                ret = remove_free_dqentry(info, buf, blk);
                if (ret >= 0)
                        ret = put_free_dqblk(info, buf, blk);
                if (ret < 0) {
                        quota_error(dquot->dq_sb, "Can't move quota data block "
                                    "(%u) to free list", blk);
                        goto out_buf;
                }
        } else {
                memset(buf +
                       (dquot->dq_off & ((1 << info->dqi_blocksize_bits) - 1)),
                       0, info->dqi_entry_size);
                if (le16_to_cpu(dh->dqdh_entries) ==
                    qtree_dqstr_in_blk(info) - 1) {
                        /* Insert will write block itself */
                        ret = insert_free_dqentry(info, buf, blk);
                        if (ret < 0) {
                                quota_error(dquot->dq_sb, "Can't insert quota "
                                    "data block (%u) to free entry list", blk);
                                goto out_buf;
                        }
                } else {
                        ret = write_blk(info, blk, buf);
                        if (ret < 0) {
                                quota_error(dquot->dq_sb, "Can't write quota "
                                            "data block %u", blk);
                                goto out_buf;
                        }
                }
        }
        dquot->dq_off = 0;      /* Quota is now unattached */
out_buf:
        kfree(buf);
        return ret;
}

/* Remove reference to dquot from tree */
static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
                       uint *blks, int depth)
{
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        int ret = 0;
        uint newblk;
        __le32 *ref = (__le32 *)buf;
        int i;

        if (!buf)
                return -ENOMEM;
        ret = read_blk(info, blks[depth], buf);
        if (ret < 0) {
                quota_error(dquot->dq_sb, "Can't read quota data block %u",
                            blks[depth]);
                goto out_buf;
        }
        newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
        ret = do_check_range(dquot->dq_sb, "block", newblk, QT_TREEOFF,
                             info->dqi_blocks - 1);
        if (ret)
                goto out_buf;

        for (i = 0; i <= depth; i++)
                if (newblk == blks[i]) {
                        quota_error(dquot->dq_sb,
                                "Cycle in quota tree detected: block %u index %u",
                                blks[depth],
                                get_index(info, dquot->dq_id, depth));
                        ret = -EIO;
                        goto out_buf;
                }
        if (depth == info->dqi_qtree_depth - 1) {
                ret = free_dqentry(info, dquot, newblk);
                blks[depth + 1] = 0;
        } else {
                blks[depth + 1] = newblk;
                ret = remove_tree(info, dquot, blks, depth + 1);
        }
        if (ret >= 0 && !blks[depth + 1]) {
                ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
                /* Block got empty? */
                for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
                        ;
                /* Don't put the root block into the free block list */
                if (i == (info->dqi_usable_bs >> 2)
                    && blks[depth] != QT_TREEOFF) {
                        put_free_dqblk(info, buf, blks[depth]);
                        blks[depth] = 0;
                } else {
                        ret = write_blk(info, blks[depth], buf);
                        if (ret < 0)
                                quota_error(dquot->dq_sb,
                                            "Can't write quota tree block %u",
                                            blks[depth]);
                }
        }
out_buf:
        kfree(buf);
        return ret;
}

/* Delete dquot from tree */
int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
        uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };

        if (!dquot->dq_off)     /* Even not allocated? */
                return 0;
        if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
                quota_error(dquot->dq_sb, "Quota tree depth too big!");
                return -EIO;
        }
        return remove_tree(info, dquot, blks, 0);
}
EXPORT_SYMBOL(qtree_delete_dquot);

/* Find entry in block */
static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
                                 struct dquot *dquot, uint blk)
{
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        loff_t ret = 0;
        int i;
        char *ddquot;

        if (!buf)
                return -ENOMEM;
        ret = read_blk(info, blk, buf);
        if (ret < 0) {
                quota_error(dquot->dq_sb, "Can't read quota tree "
                            "block %u", blk);
                goto out_buf;
        }
        ddquot = buf + sizeof(struct qt_disk_dqdbheader);
        for (i = 0; i < qtree_dqstr_in_blk(info); i++) {
                if (info->dqi_ops->is_id(ddquot, dquot))
                        break;
                ddquot += info->dqi_entry_size;
        }
        if (i == qtree_dqstr_in_blk(info)) {
                quota_error(dquot->dq_sb,
                            "Quota for id %u referenced but not present",
                            from_kqid(&init_user_ns, dquot->dq_id));
                ret = -EIO;
                goto out_buf;
        } else {
                ret = ((loff_t)blk << info->dqi_blocksize_bits) + sizeof(struct
                  qt_disk_dqdbheader) + i * info->dqi_entry_size;
        }
out_buf:
        kfree(buf);
        return ret;
}

/* Find entry for given id in the tree */
static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
                                struct dquot *dquot, uint *blks, int depth)
{
        char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        loff_t ret = 0;
        __le32 *ref = (__le32 *)buf;
        uint blk;
        int i;

        if (!buf)
                return -ENOMEM;
        ret = read_blk(info, blks[depth], buf);
        if (ret < 0) {
                quota_error(dquot->dq_sb, "Can't read quota tree block %u",
                            blks[depth]);
                goto out_buf;
        }
        ret = 0;
        blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
        if (!blk)       /* No reference? */
                goto out_buf;
        ret = do_check_range(dquot->dq_sb, "block", blk, QT_TREEOFF,
                             info->dqi_blocks - 1);
        if (ret)
                goto out_buf;

        /* Check for cycles in the tree */
        for (i = 0; i <= depth; i++)
                if (blk == blks[i]) {
                        quota_error(dquot->dq_sb,
                                "Cycle in quota tree detected: block %u index %u",
                                blks[depth],
                                get_index(info, dquot->dq_id, depth));
                        ret = -EIO;
                        goto out_buf;
                }
        blks[depth + 1] = blk;
        if (depth < info->dqi_qtree_depth - 1)
                ret = find_tree_dqentry(info, dquot, blks, depth + 1);
        else
                ret = find_block_dqentry(info, dquot, blk);
out_buf:
        kfree(buf);
        return ret;
}

/* Find entry for given id in the tree - wrapper function */
static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,
                                  struct dquot *dquot)
{
        uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };

        if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
                quota_error(dquot->dq_sb, "Quota tree depth too big!");
                return -EIO;
        }
        return find_tree_dqentry(info, dquot, blks, 0);
}

int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
        int type = dquot->dq_id.type;
        struct super_block *sb = dquot->dq_sb;
        loff_t offset;
        char *ddquot;
        int ret = 0;

#ifdef __QUOTA_QT_PARANOIA
        /* Invalidated quota? */
        if (!sb_dqopt(dquot->dq_sb)->files[type]) {
                quota_error(sb, "Quota invalidated while reading!");
                return -EIO;
        }
#endif
        /* Do we know offset of the dquot entry in the quota file? */
        if (!dquot->dq_off) {
                offset = find_dqentry(info, dquot);
                if (offset <= 0) {      /* Entry not present? */
                        if (offset < 0)
                                quota_error(sb,"Can't read quota structure "
                                            "for id %u",
                                            from_kqid(&init_user_ns,
                                                      dquot->dq_id));
                        dquot->dq_off = 0;
                        set_bit(DQ_FAKE_B, &dquot->dq_flags);
                        memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
                        ret = offset;
                        goto out;
                }
                dquot->dq_off = offset;
        }
        ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
        if (!ddquot)
                return -ENOMEM;
        ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size,
                                   dquot->dq_off);
        if (ret != info->dqi_entry_size) {
                if (ret >= 0)
                        ret = -EIO;
                quota_error(sb, "Error while reading quota structure for id %u",
                            from_kqid(&init_user_ns, dquot->dq_id));
                set_bit(DQ_FAKE_B, &dquot->dq_flags);
                memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
                kfree(ddquot);
                goto out;
        }
        spin_lock(&dquot->dq_dqb_lock);
        info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
        if (!dquot->dq_dqb.dqb_bhardlimit &&
            !dquot->dq_dqb.dqb_bsoftlimit &&
            !dquot->dq_dqb.dqb_ihardlimit &&
            !dquot->dq_dqb.dqb_isoftlimit)
                set_bit(DQ_FAKE_B, &dquot->dq_flags);
        spin_unlock(&dquot->dq_dqb_lock);
        kfree(ddquot);
out:
        dqstats_inc(DQST_READS);
        return ret;
}
EXPORT_SYMBOL(qtree_read_dquot);

/* Check whether dquot should not be deleted. We know we are
 * the only one operating on dquot (thanks to dq_lock) */
int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
        if (test_bit(DQ_FAKE_B, &dquot->dq_flags) &&
            !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
                return qtree_delete_dquot(info, 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 = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
        __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++) {
                uint blk_no = le32_to_cpu(ref[i]);

                if (blk_no == 0) {
                        *id += level_inc;
                        continue;
                }
                ret = do_check_range(info->dqi_sb, "block", blk_no, 0,
                                     info->dqi_blocks - 1);
                if (ret)
                        goto out_buf;
                if (depth == info->dqi_qtree_depth - 1) {
                        ret = 0;
                        goto out_buf;
                }
                ret = find_next_id(info, id, blk_no, 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);