| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *	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"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define __QUOTA_QT_PARANOIA
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned int epb = info->dqi_usable_bs >> 2; | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 	qid_t id = from_kqid(&init_user_ns, qid); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	depth = info->dqi_qtree_depth - depth - 1; | 
					
						
							|  |  |  | 	while (depth--) | 
					
						
							|  |  |  | 		id /= epb; | 
					
						
							|  |  |  | 	return id % epb; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Number of entries in one blocks */ | 
					
						
							| 
									
										
										
										
											2009-01-27 01:47:11 +01:00
										 |  |  | static int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader)) | 
					
						
							|  |  |  | 	       / info->dqi_entry_size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | static char *getdqbuf(size_t size) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = kmalloc(size, GFP_NOFS); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	if (!buf) | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | 		printk(KERN_WARNING | 
					
						
							|  |  |  | 		       "VFS: Not enough memory for quota buffers.\n"); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return buf; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-27 01:47:11 +01:00
										 |  |  | static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct super_block *sb = info->dqi_sb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(buf, 0, info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	return sb->s_op->quota_read(sb, info->dqi_type, buf, | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	       info->dqi_usable_bs, blk << info->dqi_blocksize_bits); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-27 01:47:11 +01:00
										 |  |  | static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct super_block *sb = info->dqi_sb; | 
					
						
							| 
									
										
										
										
											2010-05-17 18:36:03 +02:00
										 |  |  | 	ssize_t ret; | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-17 18:36:03 +02:00
										 |  |  | 	ret = sb->s_op->quota_write(sb, info->dqi_type, buf, | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	       info->dqi_usable_bs, blk << info->dqi_blocksize_bits); | 
					
						
							| 
									
										
										
										
											2010-05-17 18:36:03 +02:00
										 |  |  | 	if (ret != info->dqi_usable_bs) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(sb, "dquota write failed"); | 
					
						
							| 
									
										
										
										
											2010-05-17 18:36:03 +02:00
										 |  |  | 		if (ret >= 0) | 
					
						
							|  |  |  | 			ret = -EIO; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Remove empty block from list and return it */ | 
					
						
							|  |  |  | static int get_free_dqblk(struct qtree_mem_dqinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 		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: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Insert empty block to the list */ | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	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 */ | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, | 
					
						
							|  |  |  | 			       uint blk) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *tmpbuf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(tmpbuf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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) | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(info->dqi_sb, "Can't write block (%u) " | 
					
						
							|  |  |  | 			    "with free entries", blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(tmpbuf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Insert given block to the beginning of list with free entries */ | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, | 
					
						
							|  |  |  | 			       uint blk) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *tmpbuf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(tmpbuf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	info->dqi_free_entry = blk; | 
					
						
							|  |  |  | 	mark_info_dirty(info->dqi_sb, info->dqi_type); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(tmpbuf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		blk = get_free_dqblk(info); | 
					
						
							|  |  |  | 		if ((int)blk < 0) { | 
					
						
							|  |  |  | 			*err = blk; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 			kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		memset(buf, 0, info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | 		/* This is enough as the block is already zeroed and the entry
 | 
					
						
							|  |  |  | 		 * list is empty... */ | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		info->dqi_free_entry = blk; | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 		mark_info_dirty(dquot->dq_sb, dquot->dq_id.type); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	/* 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) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 			quota_error(dquot->dq_sb, "Can't remove block (%u) " | 
					
						
							|  |  |  | 				    "from entry free list", blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 			goto out_buf; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	le16_add_cpu(&dh->dqdh_entries, 1); | 
					
						
							|  |  |  | 	/* Find free structure in block */ | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | #ifdef __QUOTA_QT_PARANOIA
 | 
					
						
							|  |  |  | 	if (i == qtree_dqstr_in_blk(info)) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(dquot->dq_sb, "Data block full but it shouldn't"); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		*err = -EIO; | 
					
						
							|  |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	*err = write_blk(info, blk, buf); | 
					
						
							|  |  |  | 	if (*err < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(dquot->dq_sb, "Can't write quota data block %u", | 
					
						
							|  |  |  | 			    blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dquot->dq_off = (blk << info->dqi_blocksize_bits) + | 
					
						
							|  |  |  | 			sizeof(struct qt_disk_dqdbheader) + | 
					
						
							|  |  |  | 			i * info->dqi_entry_size; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return blk; | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Insert reference to structure into the trie */ | 
					
						
							|  |  |  | static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, | 
					
						
							|  |  |  | 			  uint *treeblk, int depth) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	int ret = 0, newson = 0, newact = 0; | 
					
						
							|  |  |  | 	__le32 *ref; | 
					
						
							|  |  |  | 	uint newblk; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!buf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	if (!*treeblk) { | 
					
						
							|  |  |  | 		ret = get_free_dqblk(info); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			goto out_buf; | 
					
						
							|  |  |  | 		*treeblk = ret; | 
					
						
							|  |  |  | 		memset(buf, 0, info->dqi_usable_bs); | 
					
						
							|  |  |  | 		newact = 1; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = read_blk(info, *treeblk, buf); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 			quota_error(dquot->dq_sb, "Can't read tree quota " | 
					
						
							|  |  |  | 				    "block %u", *treeblk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 			goto out_buf; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ref = (__le32 *)buf; | 
					
						
							|  |  |  | 	newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | 
					
						
							|  |  |  | 	if (!newblk) | 
					
						
							|  |  |  | 		newson = 1; | 
					
						
							|  |  |  | 	if (depth == info->dqi_qtree_depth - 1) { | 
					
						
							|  |  |  | #ifdef __QUOTA_QT_PARANOIA
 | 
					
						
							|  |  |  | 		if (newblk) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 			quota_error(dquot->dq_sb, "Inserting already present " | 
					
						
							|  |  |  | 				    "quota entry (block %u)", | 
					
						
							|  |  |  | 				    le32_to_cpu(ref[get_index(info, | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 						dquot->dq_id, depth)])); | 
					
						
							|  |  |  | 			ret = -EIO; | 
					
						
							|  |  |  | 			goto out_buf; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		newblk = find_free_dqentry(info, dquot, &ret); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = do_insert_tree(info, dquot, &newblk, depth+1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (newson && ret >= 0) { | 
					
						
							|  |  |  | 		ref[get_index(info, dquot->dq_id, depth)] = | 
					
						
							|  |  |  | 							cpu_to_le32(newblk); | 
					
						
							|  |  |  | 		ret = write_blk(info, *treeblk, buf); | 
					
						
							|  |  |  | 	} else if (newact && ret < 0) { | 
					
						
							|  |  |  | 		put_free_dqblk(info, buf, *treeblk); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Wrapper for inserting quota structure into tree */ | 
					
						
							|  |  |  | static inline int dq_insert_tree(struct qtree_mem_dqinfo *info, | 
					
						
							|  |  |  | 				 struct dquot *dquot) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int tmp = QT_TREEOFF; | 
					
						
							|  |  |  | 	return do_insert_tree(info, dquot, &tmp, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  |  * We don't have to be afraid of deadlocks as we never have quotas on quota | 
					
						
							|  |  |  |  * files... | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 	int type = dquot->dq_id.type; | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	struct super_block *sb = dquot->dq_sb; | 
					
						
							|  |  |  | 	ssize_t ret; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *ddquot = getdqbuf(info->dqi_entry_size); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!ddquot) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* dq_off is guarded by dqio_mutex */ | 
					
						
							|  |  |  | 	if (!dquot->dq_off) { | 
					
						
							|  |  |  | 		ret = dq_insert_tree(info, dquot); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 			quota_error(sb, "Error %zd occurred while creating " | 
					
						
							|  |  |  | 				    "quota", ret); | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 			kfree(ddquot); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	spin_lock(&dq_data_lock); | 
					
						
							|  |  |  | 	info->dqi_ops->mem2disk_dqblk(ddquot, dquot); | 
					
						
							|  |  |  | 	spin_unlock(&dq_data_lock); | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size, | 
					
						
							|  |  |  | 				    dquot->dq_off); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	if (ret != info->dqi_entry_size) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(sb, "dquota write failed"); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		if (ret >= 0) | 
					
						
							|  |  |  | 			ret = -ENOSPC; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-04-26 20:03:33 +04:00
										 |  |  | 	dqstats_inc(DQST_WRITES); | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(ddquot); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!buf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		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)); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret = read_blk(info, blk, buf); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(dquot->dq_sb, "Can't read quota data block %u", | 
					
						
							|  |  |  | 			    blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dh = (struct qt_disk_dqdbheader *)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) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 			quota_error(dquot->dq_sb, "Can't move quota data block " | 
					
						
							|  |  |  | 				    "(%u) to free list", blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 			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) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 				quota_error(dquot->dq_sb, "Can't insert quota " | 
					
						
							|  |  |  | 				    "data block (%u) to free entry list", blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 				goto out_buf; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ret = write_blk(info, blk, buf); | 
					
						
							|  |  |  | 			if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 				quota_error(dquot->dq_sb, "Can't write quota " | 
					
						
							|  |  |  | 					    "data block %u", blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 				goto out_buf; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dquot->dq_off = 0;	/* Quota is now unattached */ | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Remove reference to dquot from tree */ | 
					
						
							|  |  |  | static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, | 
					
						
							|  |  |  | 		       uint *blk, int depth) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	uint newblk; | 
					
						
							|  |  |  | 	__le32 *ref = (__le32 *)buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!buf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	ret = read_blk(info, *blk, buf); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-11-23 18:49:54 -08:00
										 |  |  | 		quota_error(dquot->dq_sb, "Can't read quota data block %u", | 
					
						
							|  |  |  | 			    *blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | 
					
						
							|  |  |  | 	if (depth == info->dqi_qtree_depth - 1) { | 
					
						
							|  |  |  | 		ret = free_dqentry(info, dquot, newblk); | 
					
						
							|  |  |  | 		newblk = 0; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = remove_tree(info, dquot, &newblk, depth+1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ret >= 0 && !newblk) { | 
					
						
							|  |  |  | 		int i; | 
					
						
							|  |  |  | 		ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0); | 
					
						
							|  |  |  | 		/* Block got empty? */ | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 		for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++) | 
					
						
							|  |  |  | 			; | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		/* Don't put the root block into the free block list */ | 
					
						
							|  |  |  | 		if (i == (info->dqi_usable_bs >> 2) | 
					
						
							|  |  |  | 		    && *blk != QT_TREEOFF) { | 
					
						
							|  |  |  | 			put_free_dqblk(info, buf, *blk); | 
					
						
							|  |  |  | 			*blk = 0; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ret = write_blk(info, *blk, buf); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							| 
									
										
										
										
											2010-11-23 18:49:54 -08:00
										 |  |  | 				quota_error(dquot->dq_sb, | 
					
						
							|  |  |  | 					    "Can't write quota tree block %u", | 
					
						
							|  |  |  | 					    *blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Delete dquot from tree */ | 
					
						
							|  |  |  | int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint tmp = QT_TREEOFF; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!dquot->dq_off)	/* Even not allocated? */ | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	return remove_tree(info, dquot, &tmp, 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	loff_t ret = 0; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	char *ddquot; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!buf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	ret = read_blk(info, blk, buf); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(dquot->dq_sb, "Can't read quota tree " | 
					
						
							|  |  |  | 			    "block %u", blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	if (i == qtree_dqstr_in_blk(info)) { | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 		quota_error(dquot->dq_sb, | 
					
						
							|  |  |  | 			    "Quota for id %u referenced but not present", | 
					
						
							|  |  |  | 			    from_kqid(&init_user_ns, dquot->dq_id)); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		ret = -EIO; | 
					
						
							|  |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = (blk << info->dqi_blocksize_bits) + sizeof(struct | 
					
						
							|  |  |  | 		  qt_disk_dqdbheader) + i * info->dqi_entry_size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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 blk, int depth) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *buf = getdqbuf(info->dqi_usable_bs); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	loff_t ret = 0; | 
					
						
							|  |  |  | 	__le32 *ref = (__le32 *)buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!buf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	ret = read_blk(info, blk, buf); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(dquot->dq_sb, "Can't read quota tree block %u", | 
					
						
							|  |  |  | 			    blk); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret = 0; | 
					
						
							|  |  |  | 	blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | 
					
						
							|  |  |  | 	if (!blk)	/* No reference? */ | 
					
						
							|  |  |  | 		goto out_buf; | 
					
						
							|  |  |  | 	if (depth < info->dqi_qtree_depth - 1) | 
					
						
							|  |  |  | 		ret = find_tree_dqentry(info, dquot, blk, depth+1); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		ret = find_block_dqentry(info, dquot, blk); | 
					
						
							|  |  |  | out_buf: | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(buf); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return find_tree_dqentry(info, dquot, QT_TREEOFF, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 	int type = dquot->dq_id.type; | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	struct super_block *sb = dquot->dq_sb; | 
					
						
							|  |  |  | 	loff_t offset; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	char *ddquot; | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __QUOTA_QT_PARANOIA
 | 
					
						
							|  |  |  | 	/* Invalidated quota? */ | 
					
						
							|  |  |  | 	if (!sb_dqopt(dquot->dq_sb)->files[type]) { | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(sb, "Quota invalidated while reading!"); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		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) | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 				quota_error(sb,"Can't read quota structure " | 
					
						
							|  |  |  | 					    "for id %u", | 
					
						
							|  |  |  | 					    from_kqid(&init_user_ns, | 
					
						
							|  |  |  | 						      dquot->dq_id)); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 			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 = getdqbuf(info->dqi_entry_size); | 
					
						
							|  |  |  | 	if (!ddquot) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size, | 
					
						
							|  |  |  | 				   dquot->dq_off); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	if (ret != info->dqi_entry_size) { | 
					
						
							|  |  |  | 		if (ret >= 0) | 
					
						
							|  |  |  | 			ret = -EIO; | 
					
						
							| 
									
										
										
										
											2010-07-20 16:54:43 +02:00
										 |  |  | 		quota_error(sb, "Error while reading quota structure for id %u", | 
					
						
							| 
									
										
										
										
											2012-09-16 03:56:19 -07:00
										 |  |  | 			    from_kqid(&init_user_ns, dquot->dq_id)); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		set_bit(DQ_FAKE_B, &dquot->dq_flags); | 
					
						
							|  |  |  | 		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 		kfree(ddquot); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	spin_lock(&dq_data_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(&dq_data_lock); | 
					
						
							| 
									
										
										
										
											2009-01-26 16:17:50 +01:00
										 |  |  | 	kfree(ddquot); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | out: | 
					
						
							| 
									
										
										
										
											2010-04-26 20:03:33 +04:00
										 |  |  | 	dqstats_inc(DQST_READS); | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 	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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-27 15:47:22 +01:00
										 |  |  | 	if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && | 
					
						
							|  |  |  | 	    !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) | 
					
						
							| 
									
										
										
										
											2008-09-22 05:54:49 +02:00
										 |  |  | 		return qtree_delete_dquot(info, dquot); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(qtree_release_dquot); |