162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vfsv0 quota IO operations on file 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/errno.h> 762306a36Sopenharmony_ci#include <linux/fs.h> 862306a36Sopenharmony_ci#include <linux/mount.h> 962306a36Sopenharmony_ci#include <linux/dqblk_v2.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/quotaops.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/byteorder.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "quota_tree.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciMODULE_AUTHOR("Jan Kara"); 2162306a36Sopenharmony_ciMODULE_DESCRIPTION("Quota trie support"); 2262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define __QUOTA_QT_PARANOIA 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci unsigned int epb = info->dqi_usable_bs >> 2; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci depth = info->dqi_qtree_depth - depth - 1; 3162306a36Sopenharmony_ci while (depth--) 3262306a36Sopenharmony_ci id /= epb; 3362306a36Sopenharmony_ci return id % epb; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci qid_t id = from_kqid(&init_user_ns, qid); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return __get_index(info, id, depth); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Number of entries in one blocks */ 4462306a36Sopenharmony_cistatic int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader)) 4762306a36Sopenharmony_ci / info->dqi_entry_size; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct super_block *sb = info->dqi_sb; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci memset(buf, 0, info->dqi_usable_bs); 5562306a36Sopenharmony_ci return sb->s_op->quota_read(sb, info->dqi_type, buf, 5662306a36Sopenharmony_ci info->dqi_usable_bs, (loff_t)blk << info->dqi_blocksize_bits); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct super_block *sb = info->dqi_sb; 6262306a36Sopenharmony_ci ssize_t ret; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci ret = sb->s_op->quota_write(sb, info->dqi_type, buf, 6562306a36Sopenharmony_ci info->dqi_usable_bs, (loff_t)blk << info->dqi_blocksize_bits); 6662306a36Sopenharmony_ci if (ret != info->dqi_usable_bs) { 6762306a36Sopenharmony_ci quota_error(sb, "dquota write failed"); 6862306a36Sopenharmony_ci if (ret >= 0) 6962306a36Sopenharmony_ci ret = -EIO; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci return ret; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic inline int do_check_range(struct super_block *sb, const char *val_name, 7562306a36Sopenharmony_ci uint val, uint min_val, uint max_val) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (val < min_val || val > max_val) { 7862306a36Sopenharmony_ci quota_error(sb, "Getting %s %u out of range %u-%u", 7962306a36Sopenharmony_ci val_name, val, min_val, max_val); 8062306a36Sopenharmony_ci return -EUCLEAN; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int check_dquot_block_header(struct qtree_mem_dqinfo *info, 8762306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int err = 0; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci err = do_check_range(info->dqi_sb, "dqdh_next_free", 9262306a36Sopenharmony_ci le32_to_cpu(dh->dqdh_next_free), 0, 9362306a36Sopenharmony_ci info->dqi_blocks - 1); 9462306a36Sopenharmony_ci if (err) 9562306a36Sopenharmony_ci return err; 9662306a36Sopenharmony_ci err = do_check_range(info->dqi_sb, "dqdh_prev_free", 9762306a36Sopenharmony_ci le32_to_cpu(dh->dqdh_prev_free), 0, 9862306a36Sopenharmony_ci info->dqi_blocks - 1); 9962306a36Sopenharmony_ci if (err) 10062306a36Sopenharmony_ci return err; 10162306a36Sopenharmony_ci err = do_check_range(info->dqi_sb, "dqdh_entries", 10262306a36Sopenharmony_ci le16_to_cpu(dh->dqdh_entries), 0, 10362306a36Sopenharmony_ci qtree_dqstr_in_blk(info)); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return err; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Remove empty block from list and return it */ 10962306a36Sopenharmony_cistatic int get_free_dqblk(struct qtree_mem_dqinfo *info) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 11262306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; 11362306a36Sopenharmony_ci int ret, blk; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!buf) 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci if (info->dqi_free_blk) { 11862306a36Sopenharmony_ci blk = info->dqi_free_blk; 11962306a36Sopenharmony_ci ret = read_blk(info, blk, buf); 12062306a36Sopenharmony_ci if (ret < 0) 12162306a36Sopenharmony_ci goto out_buf; 12262306a36Sopenharmony_ci ret = check_dquot_block_header(info, dh); 12362306a36Sopenharmony_ci if (ret) 12462306a36Sopenharmony_ci goto out_buf; 12562306a36Sopenharmony_ci info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci else { 12862306a36Sopenharmony_ci memset(buf, 0, info->dqi_usable_bs); 12962306a36Sopenharmony_ci /* Assure block allocation... */ 13062306a36Sopenharmony_ci ret = write_blk(info, info->dqi_blocks, buf); 13162306a36Sopenharmony_ci if (ret < 0) 13262306a36Sopenharmony_ci goto out_buf; 13362306a36Sopenharmony_ci blk = info->dqi_blocks++; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci mark_info_dirty(info->dqi_sb, info->dqi_type); 13662306a36Sopenharmony_ci ret = blk; 13762306a36Sopenharmony_ciout_buf: 13862306a36Sopenharmony_ci kfree(buf); 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* Insert empty block to the list */ 14362306a36Sopenharmony_cistatic int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; 14662306a36Sopenharmony_ci int err; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk); 14962306a36Sopenharmony_ci dh->dqdh_prev_free = cpu_to_le32(0); 15062306a36Sopenharmony_ci dh->dqdh_entries = cpu_to_le16(0); 15162306a36Sopenharmony_ci err = write_blk(info, blk, buf); 15262306a36Sopenharmony_ci if (err < 0) 15362306a36Sopenharmony_ci return err; 15462306a36Sopenharmony_ci info->dqi_free_blk = blk; 15562306a36Sopenharmony_ci mark_info_dirty(info->dqi_sb, info->dqi_type); 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Remove given block from the list of blocks with free entries */ 16062306a36Sopenharmony_cistatic int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, 16162306a36Sopenharmony_ci uint blk) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 16462306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; 16562306a36Sopenharmony_ci uint nextblk = le32_to_cpu(dh->dqdh_next_free); 16662306a36Sopenharmony_ci uint prevblk = le32_to_cpu(dh->dqdh_prev_free); 16762306a36Sopenharmony_ci int err; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!tmpbuf) 17062306a36Sopenharmony_ci return -ENOMEM; 17162306a36Sopenharmony_ci if (nextblk) { 17262306a36Sopenharmony_ci err = read_blk(info, nextblk, tmpbuf); 17362306a36Sopenharmony_ci if (err < 0) 17462306a36Sopenharmony_ci goto out_buf; 17562306a36Sopenharmony_ci ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = 17662306a36Sopenharmony_ci dh->dqdh_prev_free; 17762306a36Sopenharmony_ci err = write_blk(info, nextblk, tmpbuf); 17862306a36Sopenharmony_ci if (err < 0) 17962306a36Sopenharmony_ci goto out_buf; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (prevblk) { 18262306a36Sopenharmony_ci err = read_blk(info, prevblk, tmpbuf); 18362306a36Sopenharmony_ci if (err < 0) 18462306a36Sopenharmony_ci goto out_buf; 18562306a36Sopenharmony_ci ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free = 18662306a36Sopenharmony_ci dh->dqdh_next_free; 18762306a36Sopenharmony_ci err = write_blk(info, prevblk, tmpbuf); 18862306a36Sopenharmony_ci if (err < 0) 18962306a36Sopenharmony_ci goto out_buf; 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci info->dqi_free_entry = nextblk; 19262306a36Sopenharmony_ci mark_info_dirty(info->dqi_sb, info->dqi_type); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci kfree(tmpbuf); 19562306a36Sopenharmony_ci dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); 19662306a36Sopenharmony_ci /* No matter whether write succeeds block is out of list */ 19762306a36Sopenharmony_ci if (write_blk(info, blk, buf) < 0) 19862306a36Sopenharmony_ci quota_error(info->dqi_sb, "Can't write block (%u) " 19962306a36Sopenharmony_ci "with free entries", blk); 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ciout_buf: 20262306a36Sopenharmony_ci kfree(tmpbuf); 20362306a36Sopenharmony_ci return err; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* Insert given block to the beginning of list with free entries */ 20762306a36Sopenharmony_cistatic int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, 20862306a36Sopenharmony_ci uint blk) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 21162306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; 21262306a36Sopenharmony_ci int err; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!tmpbuf) 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry); 21762306a36Sopenharmony_ci dh->dqdh_prev_free = cpu_to_le32(0); 21862306a36Sopenharmony_ci err = write_blk(info, blk, buf); 21962306a36Sopenharmony_ci if (err < 0) 22062306a36Sopenharmony_ci goto out_buf; 22162306a36Sopenharmony_ci if (info->dqi_free_entry) { 22262306a36Sopenharmony_ci err = read_blk(info, info->dqi_free_entry, tmpbuf); 22362306a36Sopenharmony_ci if (err < 0) 22462306a36Sopenharmony_ci goto out_buf; 22562306a36Sopenharmony_ci ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = 22662306a36Sopenharmony_ci cpu_to_le32(blk); 22762306a36Sopenharmony_ci err = write_blk(info, info->dqi_free_entry, tmpbuf); 22862306a36Sopenharmony_ci if (err < 0) 22962306a36Sopenharmony_ci goto out_buf; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci kfree(tmpbuf); 23262306a36Sopenharmony_ci info->dqi_free_entry = blk; 23362306a36Sopenharmony_ci mark_info_dirty(info->dqi_sb, info->dqi_type); 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ciout_buf: 23662306a36Sopenharmony_ci kfree(tmpbuf); 23762306a36Sopenharmony_ci return err; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* Is the entry in the block free? */ 24162306a36Sopenharmony_ciint qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int i; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (i = 0; i < info->dqi_entry_size; i++) 24662306a36Sopenharmony_ci if (disk[i]) 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci return 1; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ciEXPORT_SYMBOL(qtree_entry_unused); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* Find space for dquot */ 25362306a36Sopenharmony_cistatic uint find_free_dqentry(struct qtree_mem_dqinfo *info, 25462306a36Sopenharmony_ci struct dquot *dquot, int *err) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci uint blk, i; 25762306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh; 25862306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 25962306a36Sopenharmony_ci char *ddquot; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci *err = 0; 26262306a36Sopenharmony_ci if (!buf) { 26362306a36Sopenharmony_ci *err = -ENOMEM; 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci dh = (struct qt_disk_dqdbheader *)buf; 26762306a36Sopenharmony_ci if (info->dqi_free_entry) { 26862306a36Sopenharmony_ci blk = info->dqi_free_entry; 26962306a36Sopenharmony_ci *err = read_blk(info, blk, buf); 27062306a36Sopenharmony_ci if (*err < 0) 27162306a36Sopenharmony_ci goto out_buf; 27262306a36Sopenharmony_ci *err = check_dquot_block_header(info, dh); 27362306a36Sopenharmony_ci if (*err) 27462306a36Sopenharmony_ci goto out_buf; 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci blk = get_free_dqblk(info); 27762306a36Sopenharmony_ci if ((int)blk < 0) { 27862306a36Sopenharmony_ci *err = blk; 27962306a36Sopenharmony_ci kfree(buf); 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci memset(buf, 0, info->dqi_usable_bs); 28362306a36Sopenharmony_ci /* This is enough as the block is already zeroed and the entry 28462306a36Sopenharmony_ci * list is empty... */ 28562306a36Sopenharmony_ci info->dqi_free_entry = blk; 28662306a36Sopenharmony_ci mark_info_dirty(dquot->dq_sb, dquot->dq_id.type); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci /* Block will be full? */ 28962306a36Sopenharmony_ci if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) { 29062306a36Sopenharmony_ci *err = remove_free_dqentry(info, buf, blk); 29162306a36Sopenharmony_ci if (*err < 0) { 29262306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't remove block (%u) " 29362306a36Sopenharmony_ci "from entry free list", blk); 29462306a36Sopenharmony_ci goto out_buf; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci le16_add_cpu(&dh->dqdh_entries, 1); 29862306a36Sopenharmony_ci /* Find free structure in block */ 29962306a36Sopenharmony_ci ddquot = buf + sizeof(struct qt_disk_dqdbheader); 30062306a36Sopenharmony_ci for (i = 0; i < qtree_dqstr_in_blk(info); i++) { 30162306a36Sopenharmony_ci if (qtree_entry_unused(info, ddquot)) 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci ddquot += info->dqi_entry_size; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci#ifdef __QUOTA_QT_PARANOIA 30662306a36Sopenharmony_ci if (i == qtree_dqstr_in_blk(info)) { 30762306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Data block full but it shouldn't"); 30862306a36Sopenharmony_ci *err = -EIO; 30962306a36Sopenharmony_ci goto out_buf; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci#endif 31262306a36Sopenharmony_ci *err = write_blk(info, blk, buf); 31362306a36Sopenharmony_ci if (*err < 0) { 31462306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't write quota data block %u", 31562306a36Sopenharmony_ci blk); 31662306a36Sopenharmony_ci goto out_buf; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci dquot->dq_off = ((loff_t)blk << info->dqi_blocksize_bits) + 31962306a36Sopenharmony_ci sizeof(struct qt_disk_dqdbheader) + 32062306a36Sopenharmony_ci i * info->dqi_entry_size; 32162306a36Sopenharmony_ci kfree(buf); 32262306a36Sopenharmony_ci return blk; 32362306a36Sopenharmony_ciout_buf: 32462306a36Sopenharmony_ci kfree(buf); 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* Insert reference to structure into the trie */ 32962306a36Sopenharmony_cistatic int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, 33062306a36Sopenharmony_ci uint *treeblk, int depth) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 33362306a36Sopenharmony_ci int ret = 0, newson = 0, newact = 0; 33462306a36Sopenharmony_ci __le32 *ref; 33562306a36Sopenharmony_ci uint newblk; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!buf) 33862306a36Sopenharmony_ci return -ENOMEM; 33962306a36Sopenharmony_ci if (!*treeblk) { 34062306a36Sopenharmony_ci ret = get_free_dqblk(info); 34162306a36Sopenharmony_ci if (ret < 0) 34262306a36Sopenharmony_ci goto out_buf; 34362306a36Sopenharmony_ci *treeblk = ret; 34462306a36Sopenharmony_ci memset(buf, 0, info->dqi_usable_bs); 34562306a36Sopenharmony_ci newact = 1; 34662306a36Sopenharmony_ci } else { 34762306a36Sopenharmony_ci ret = read_blk(info, *treeblk, buf); 34862306a36Sopenharmony_ci if (ret < 0) { 34962306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't read tree quota " 35062306a36Sopenharmony_ci "block %u", *treeblk); 35162306a36Sopenharmony_ci goto out_buf; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci ref = (__le32 *)buf; 35562306a36Sopenharmony_ci newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); 35662306a36Sopenharmony_ci ret = do_check_range(dquot->dq_sb, "block", newblk, 0, 35762306a36Sopenharmony_ci info->dqi_blocks - 1); 35862306a36Sopenharmony_ci if (ret) 35962306a36Sopenharmony_ci goto out_buf; 36062306a36Sopenharmony_ci if (!newblk) 36162306a36Sopenharmony_ci newson = 1; 36262306a36Sopenharmony_ci if (depth == info->dqi_qtree_depth - 1) { 36362306a36Sopenharmony_ci#ifdef __QUOTA_QT_PARANOIA 36462306a36Sopenharmony_ci if (newblk) { 36562306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Inserting already present " 36662306a36Sopenharmony_ci "quota entry (block %u)", 36762306a36Sopenharmony_ci le32_to_cpu(ref[get_index(info, 36862306a36Sopenharmony_ci dquot->dq_id, depth)])); 36962306a36Sopenharmony_ci ret = -EIO; 37062306a36Sopenharmony_ci goto out_buf; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci#endif 37362306a36Sopenharmony_ci newblk = find_free_dqentry(info, dquot, &ret); 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci ret = do_insert_tree(info, dquot, &newblk, depth+1); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci if (newson && ret >= 0) { 37862306a36Sopenharmony_ci ref[get_index(info, dquot->dq_id, depth)] = 37962306a36Sopenharmony_ci cpu_to_le32(newblk); 38062306a36Sopenharmony_ci ret = write_blk(info, *treeblk, buf); 38162306a36Sopenharmony_ci } else if (newact && ret < 0) { 38262306a36Sopenharmony_ci put_free_dqblk(info, buf, *treeblk); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ciout_buf: 38562306a36Sopenharmony_ci kfree(buf); 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* Wrapper for inserting quota structure into tree */ 39062306a36Sopenharmony_cistatic inline int dq_insert_tree(struct qtree_mem_dqinfo *info, 39162306a36Sopenharmony_ci struct dquot *dquot) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci int tmp = QT_TREEOFF; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci#ifdef __QUOTA_QT_PARANOIA 39662306a36Sopenharmony_ci if (info->dqi_blocks <= QT_TREEOFF) { 39762306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Quota tree root isn't allocated!"); 39862306a36Sopenharmony_ci return -EIO; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci#endif 40162306a36Sopenharmony_ci return do_insert_tree(info, dquot, &tmp, 0); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* 40562306a36Sopenharmony_ci * We don't have to be afraid of deadlocks as we never have quotas on quota 40662306a36Sopenharmony_ci * files... 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ciint qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int type = dquot->dq_id.type; 41162306a36Sopenharmony_ci struct super_block *sb = dquot->dq_sb; 41262306a36Sopenharmony_ci ssize_t ret; 41362306a36Sopenharmony_ci char *ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!ddquot) 41662306a36Sopenharmony_ci return -ENOMEM; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* dq_off is guarded by dqio_sem */ 41962306a36Sopenharmony_ci if (!dquot->dq_off) { 42062306a36Sopenharmony_ci ret = dq_insert_tree(info, dquot); 42162306a36Sopenharmony_ci if (ret < 0) { 42262306a36Sopenharmony_ci quota_error(sb, "Error %zd occurred while creating " 42362306a36Sopenharmony_ci "quota", ret); 42462306a36Sopenharmony_ci kfree(ddquot); 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci spin_lock(&dquot->dq_dqb_lock); 42962306a36Sopenharmony_ci info->dqi_ops->mem2disk_dqblk(ddquot, dquot); 43062306a36Sopenharmony_ci spin_unlock(&dquot->dq_dqb_lock); 43162306a36Sopenharmony_ci ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size, 43262306a36Sopenharmony_ci dquot->dq_off); 43362306a36Sopenharmony_ci if (ret != info->dqi_entry_size) { 43462306a36Sopenharmony_ci quota_error(sb, "dquota write failed"); 43562306a36Sopenharmony_ci if (ret >= 0) 43662306a36Sopenharmony_ci ret = -ENOSPC; 43762306a36Sopenharmony_ci } else { 43862306a36Sopenharmony_ci ret = 0; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci dqstats_inc(DQST_WRITES); 44162306a36Sopenharmony_ci kfree(ddquot); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ciEXPORT_SYMBOL(qtree_write_dquot); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci/* Free dquot entry in data block */ 44862306a36Sopenharmony_cistatic int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, 44962306a36Sopenharmony_ci uint blk) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct qt_disk_dqdbheader *dh; 45262306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 45362306a36Sopenharmony_ci int ret = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!buf) 45662306a36Sopenharmony_ci return -ENOMEM; 45762306a36Sopenharmony_ci if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { 45862306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Quota structure has offset to " 45962306a36Sopenharmony_ci "other block (%u) than it should (%u)", blk, 46062306a36Sopenharmony_ci (uint)(dquot->dq_off >> info->dqi_blocksize_bits)); 46162306a36Sopenharmony_ci ret = -EIO; 46262306a36Sopenharmony_ci goto out_buf; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci ret = read_blk(info, blk, buf); 46562306a36Sopenharmony_ci if (ret < 0) { 46662306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't read quota data block %u", 46762306a36Sopenharmony_ci blk); 46862306a36Sopenharmony_ci goto out_buf; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci dh = (struct qt_disk_dqdbheader *)buf; 47162306a36Sopenharmony_ci ret = check_dquot_block_header(info, dh); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci goto out_buf; 47462306a36Sopenharmony_ci le16_add_cpu(&dh->dqdh_entries, -1); 47562306a36Sopenharmony_ci if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ 47662306a36Sopenharmony_ci ret = remove_free_dqentry(info, buf, blk); 47762306a36Sopenharmony_ci if (ret >= 0) 47862306a36Sopenharmony_ci ret = put_free_dqblk(info, buf, blk); 47962306a36Sopenharmony_ci if (ret < 0) { 48062306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't move quota data block " 48162306a36Sopenharmony_ci "(%u) to free list", blk); 48262306a36Sopenharmony_ci goto out_buf; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci memset(buf + 48662306a36Sopenharmony_ci (dquot->dq_off & ((1 << info->dqi_blocksize_bits) - 1)), 48762306a36Sopenharmony_ci 0, info->dqi_entry_size); 48862306a36Sopenharmony_ci if (le16_to_cpu(dh->dqdh_entries) == 48962306a36Sopenharmony_ci qtree_dqstr_in_blk(info) - 1) { 49062306a36Sopenharmony_ci /* Insert will write block itself */ 49162306a36Sopenharmony_ci ret = insert_free_dqentry(info, buf, blk); 49262306a36Sopenharmony_ci if (ret < 0) { 49362306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't insert quota " 49462306a36Sopenharmony_ci "data block (%u) to free entry list", blk); 49562306a36Sopenharmony_ci goto out_buf; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci ret = write_blk(info, blk, buf); 49962306a36Sopenharmony_ci if (ret < 0) { 50062306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't write quota " 50162306a36Sopenharmony_ci "data block %u", blk); 50262306a36Sopenharmony_ci goto out_buf; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci dquot->dq_off = 0; /* Quota is now unattached */ 50762306a36Sopenharmony_ciout_buf: 50862306a36Sopenharmony_ci kfree(buf); 50962306a36Sopenharmony_ci return ret; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* Remove reference to dquot from tree */ 51362306a36Sopenharmony_cistatic int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, 51462306a36Sopenharmony_ci uint *blk, int depth) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 51762306a36Sopenharmony_ci int ret = 0; 51862306a36Sopenharmony_ci uint newblk; 51962306a36Sopenharmony_ci __le32 *ref = (__le32 *)buf; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!buf) 52262306a36Sopenharmony_ci return -ENOMEM; 52362306a36Sopenharmony_ci ret = read_blk(info, *blk, buf); 52462306a36Sopenharmony_ci if (ret < 0) { 52562306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't read quota data block %u", 52662306a36Sopenharmony_ci *blk); 52762306a36Sopenharmony_ci goto out_buf; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); 53062306a36Sopenharmony_ci ret = do_check_range(dquot->dq_sb, "block", newblk, QT_TREEOFF, 53162306a36Sopenharmony_ci info->dqi_blocks - 1); 53262306a36Sopenharmony_ci if (ret) 53362306a36Sopenharmony_ci goto out_buf; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (depth == info->dqi_qtree_depth - 1) { 53662306a36Sopenharmony_ci ret = free_dqentry(info, dquot, newblk); 53762306a36Sopenharmony_ci newblk = 0; 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci ret = remove_tree(info, dquot, &newblk, depth+1); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci if (ret >= 0 && !newblk) { 54262306a36Sopenharmony_ci int i; 54362306a36Sopenharmony_ci ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0); 54462306a36Sopenharmony_ci /* Block got empty? */ 54562306a36Sopenharmony_ci for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++) 54662306a36Sopenharmony_ci ; 54762306a36Sopenharmony_ci /* Don't put the root block into the free block list */ 54862306a36Sopenharmony_ci if (i == (info->dqi_usable_bs >> 2) 54962306a36Sopenharmony_ci && *blk != QT_TREEOFF) { 55062306a36Sopenharmony_ci put_free_dqblk(info, buf, *blk); 55162306a36Sopenharmony_ci *blk = 0; 55262306a36Sopenharmony_ci } else { 55362306a36Sopenharmony_ci ret = write_blk(info, *blk, buf); 55462306a36Sopenharmony_ci if (ret < 0) 55562306a36Sopenharmony_ci quota_error(dquot->dq_sb, 55662306a36Sopenharmony_ci "Can't write quota tree block %u", 55762306a36Sopenharmony_ci *blk); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ciout_buf: 56162306a36Sopenharmony_ci kfree(buf); 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* Delete dquot from tree */ 56662306a36Sopenharmony_ciint qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci uint tmp = QT_TREEOFF; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (!dquot->dq_off) /* Even not allocated? */ 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci return remove_tree(info, dquot, &tmp, 0); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ciEXPORT_SYMBOL(qtree_delete_dquot); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* Find entry in block */ 57762306a36Sopenharmony_cistatic loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, 57862306a36Sopenharmony_ci struct dquot *dquot, uint blk) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 58162306a36Sopenharmony_ci loff_t ret = 0; 58262306a36Sopenharmony_ci int i; 58362306a36Sopenharmony_ci char *ddquot; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!buf) 58662306a36Sopenharmony_ci return -ENOMEM; 58762306a36Sopenharmony_ci ret = read_blk(info, blk, buf); 58862306a36Sopenharmony_ci if (ret < 0) { 58962306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't read quota tree " 59062306a36Sopenharmony_ci "block %u", blk); 59162306a36Sopenharmony_ci goto out_buf; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci ddquot = buf + sizeof(struct qt_disk_dqdbheader); 59462306a36Sopenharmony_ci for (i = 0; i < qtree_dqstr_in_blk(info); i++) { 59562306a36Sopenharmony_ci if (info->dqi_ops->is_id(ddquot, dquot)) 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci ddquot += info->dqi_entry_size; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci if (i == qtree_dqstr_in_blk(info)) { 60062306a36Sopenharmony_ci quota_error(dquot->dq_sb, 60162306a36Sopenharmony_ci "Quota for id %u referenced but not present", 60262306a36Sopenharmony_ci from_kqid(&init_user_ns, dquot->dq_id)); 60362306a36Sopenharmony_ci ret = -EIO; 60462306a36Sopenharmony_ci goto out_buf; 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci ret = ((loff_t)blk << info->dqi_blocksize_bits) + sizeof(struct 60762306a36Sopenharmony_ci qt_disk_dqdbheader) + i * info->dqi_entry_size; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ciout_buf: 61062306a36Sopenharmony_ci kfree(buf); 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* Find entry for given id in the tree */ 61562306a36Sopenharmony_cistatic loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info, 61662306a36Sopenharmony_ci struct dquot *dquot, uint blk, int depth) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 61962306a36Sopenharmony_ci loff_t ret = 0; 62062306a36Sopenharmony_ci __le32 *ref = (__le32 *)buf; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!buf) 62362306a36Sopenharmony_ci return -ENOMEM; 62462306a36Sopenharmony_ci ret = read_blk(info, blk, buf); 62562306a36Sopenharmony_ci if (ret < 0) { 62662306a36Sopenharmony_ci quota_error(dquot->dq_sb, "Can't read quota tree block %u", 62762306a36Sopenharmony_ci blk); 62862306a36Sopenharmony_ci goto out_buf; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci ret = 0; 63162306a36Sopenharmony_ci blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); 63262306a36Sopenharmony_ci if (!blk) /* No reference? */ 63362306a36Sopenharmony_ci goto out_buf; 63462306a36Sopenharmony_ci ret = do_check_range(dquot->dq_sb, "block", blk, QT_TREEOFF, 63562306a36Sopenharmony_ci info->dqi_blocks - 1); 63662306a36Sopenharmony_ci if (ret) 63762306a36Sopenharmony_ci goto out_buf; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (depth < info->dqi_qtree_depth - 1) 64062306a36Sopenharmony_ci ret = find_tree_dqentry(info, dquot, blk, depth+1); 64162306a36Sopenharmony_ci else 64262306a36Sopenharmony_ci ret = find_block_dqentry(info, dquot, blk); 64362306a36Sopenharmony_ciout_buf: 64462306a36Sopenharmony_ci kfree(buf); 64562306a36Sopenharmony_ci return ret; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci/* Find entry for given id in the tree - wrapper function */ 64962306a36Sopenharmony_cistatic inline loff_t find_dqentry(struct qtree_mem_dqinfo *info, 65062306a36Sopenharmony_ci struct dquot *dquot) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci return find_tree_dqentry(info, dquot, QT_TREEOFF, 0); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ciint qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci int type = dquot->dq_id.type; 65862306a36Sopenharmony_ci struct super_block *sb = dquot->dq_sb; 65962306a36Sopenharmony_ci loff_t offset; 66062306a36Sopenharmony_ci char *ddquot; 66162306a36Sopenharmony_ci int ret = 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci#ifdef __QUOTA_QT_PARANOIA 66462306a36Sopenharmony_ci /* Invalidated quota? */ 66562306a36Sopenharmony_ci if (!sb_dqopt(dquot->dq_sb)->files[type]) { 66662306a36Sopenharmony_ci quota_error(sb, "Quota invalidated while reading!"); 66762306a36Sopenharmony_ci return -EIO; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci#endif 67062306a36Sopenharmony_ci /* Do we know offset of the dquot entry in the quota file? */ 67162306a36Sopenharmony_ci if (!dquot->dq_off) { 67262306a36Sopenharmony_ci offset = find_dqentry(info, dquot); 67362306a36Sopenharmony_ci if (offset <= 0) { /* Entry not present? */ 67462306a36Sopenharmony_ci if (offset < 0) 67562306a36Sopenharmony_ci quota_error(sb,"Can't read quota structure " 67662306a36Sopenharmony_ci "for id %u", 67762306a36Sopenharmony_ci from_kqid(&init_user_ns, 67862306a36Sopenharmony_ci dquot->dq_id)); 67962306a36Sopenharmony_ci dquot->dq_off = 0; 68062306a36Sopenharmony_ci set_bit(DQ_FAKE_B, &dquot->dq_flags); 68162306a36Sopenharmony_ci memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); 68262306a36Sopenharmony_ci ret = offset; 68362306a36Sopenharmony_ci goto out; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci dquot->dq_off = offset; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS); 68862306a36Sopenharmony_ci if (!ddquot) 68962306a36Sopenharmony_ci return -ENOMEM; 69062306a36Sopenharmony_ci ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size, 69162306a36Sopenharmony_ci dquot->dq_off); 69262306a36Sopenharmony_ci if (ret != info->dqi_entry_size) { 69362306a36Sopenharmony_ci if (ret >= 0) 69462306a36Sopenharmony_ci ret = -EIO; 69562306a36Sopenharmony_ci quota_error(sb, "Error while reading quota structure for id %u", 69662306a36Sopenharmony_ci from_kqid(&init_user_ns, dquot->dq_id)); 69762306a36Sopenharmony_ci set_bit(DQ_FAKE_B, &dquot->dq_flags); 69862306a36Sopenharmony_ci memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); 69962306a36Sopenharmony_ci kfree(ddquot); 70062306a36Sopenharmony_ci goto out; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci spin_lock(&dquot->dq_dqb_lock); 70362306a36Sopenharmony_ci info->dqi_ops->disk2mem_dqblk(dquot, ddquot); 70462306a36Sopenharmony_ci if (!dquot->dq_dqb.dqb_bhardlimit && 70562306a36Sopenharmony_ci !dquot->dq_dqb.dqb_bsoftlimit && 70662306a36Sopenharmony_ci !dquot->dq_dqb.dqb_ihardlimit && 70762306a36Sopenharmony_ci !dquot->dq_dqb.dqb_isoftlimit) 70862306a36Sopenharmony_ci set_bit(DQ_FAKE_B, &dquot->dq_flags); 70962306a36Sopenharmony_ci spin_unlock(&dquot->dq_dqb_lock); 71062306a36Sopenharmony_ci kfree(ddquot); 71162306a36Sopenharmony_ciout: 71262306a36Sopenharmony_ci dqstats_inc(DQST_READS); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ciEXPORT_SYMBOL(qtree_read_dquot); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/* Check whether dquot should not be deleted. We know we are 71862306a36Sopenharmony_ci * the only one operating on dquot (thanks to dq_lock) */ 71962306a36Sopenharmony_ciint qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && 72262306a36Sopenharmony_ci !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) 72362306a36Sopenharmony_ci return qtree_delete_dquot(info, dquot); 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ciEXPORT_SYMBOL(qtree_release_dquot); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id, 72962306a36Sopenharmony_ci unsigned int blk, int depth) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS); 73262306a36Sopenharmony_ci __le32 *ref = (__le32 *)buf; 73362306a36Sopenharmony_ci ssize_t ret; 73462306a36Sopenharmony_ci unsigned int epb = info->dqi_usable_bs >> 2; 73562306a36Sopenharmony_ci unsigned int level_inc = 1; 73662306a36Sopenharmony_ci int i; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!buf) 73962306a36Sopenharmony_ci return -ENOMEM; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci for (i = depth; i < info->dqi_qtree_depth - 1; i++) 74262306a36Sopenharmony_ci level_inc *= epb; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci ret = read_blk(info, blk, buf); 74562306a36Sopenharmony_ci if (ret < 0) { 74662306a36Sopenharmony_ci quota_error(info->dqi_sb, 74762306a36Sopenharmony_ci "Can't read quota tree block %u", blk); 74862306a36Sopenharmony_ci goto out_buf; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci for (i = __get_index(info, *id, depth); i < epb; i++) { 75162306a36Sopenharmony_ci uint blk_no = le32_to_cpu(ref[i]); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (blk_no == 0) { 75462306a36Sopenharmony_ci *id += level_inc; 75562306a36Sopenharmony_ci continue; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci ret = do_check_range(info->dqi_sb, "block", blk_no, 0, 75862306a36Sopenharmony_ci info->dqi_blocks - 1); 75962306a36Sopenharmony_ci if (ret) 76062306a36Sopenharmony_ci goto out_buf; 76162306a36Sopenharmony_ci if (depth == info->dqi_qtree_depth - 1) { 76262306a36Sopenharmony_ci ret = 0; 76362306a36Sopenharmony_ci goto out_buf; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci ret = find_next_id(info, id, blk_no, depth + 1); 76662306a36Sopenharmony_ci if (ret != -ENOENT) 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci if (i == epb) { 77062306a36Sopenharmony_ci ret = -ENOENT; 77162306a36Sopenharmony_ci goto out_buf; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ciout_buf: 77462306a36Sopenharmony_ci kfree(buf); 77562306a36Sopenharmony_ci return ret; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ciint qtree_get_next_id(struct qtree_mem_dqinfo *info, struct kqid *qid) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci qid_t id = from_kqid(&init_user_ns, *qid); 78162306a36Sopenharmony_ci int ret; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ret = find_next_id(info, &id, QT_TREEOFF, 0); 78462306a36Sopenharmony_ci if (ret < 0) 78562306a36Sopenharmony_ci return ret; 78662306a36Sopenharmony_ci *qid = make_kqid(&init_user_ns, qid->type, id); 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ciEXPORT_SYMBOL(qtree_get_next_id); 790