dquot: Detect partial write error to quota file in write_blk() and add printk_ratelimit for quota error messages

This patch changes quota_tree.c:write_blk() to detect error caused by partial
write to quota file and add a macro to limit control printed quota error
messages so we won't fill up dmesg with a corrupted quota file.

Signed-off-by: Jiaying Zhang <jiayingz@google.com>
Signed-off-by: Jan Kara <jack@suse.cz>
diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index 5b7f741..24f0340 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -60,9 +60,17 @@
 static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
 {
 	struct super_block *sb = info->dqi_sb;
+	ssize_t ret;
 
-	return sb->s_op->quota_write(sb, info->dqi_type, buf,
+	ret = sb->s_op->quota_write(sb, info->dqi_type, buf,
 	       info->dqi_usable_bs, blk << info->dqi_blocksize_bits);
+	if (ret != info->dqi_usable_bs) {
+		q_warn(KERN_WARNING "VFS: dquota write failed on "
+			"dev %s\n", sb->s_id);
+		if (ret >= 0)
+			ret = -EIO;
+	}
+	return ret;
 }
 
 /* Remove empty block from list and return it */
@@ -152,7 +160,7 @@
 	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)
-		printk(KERN_ERR
+		q_warn(KERN_ERR
 		       "VFS: Can't write block (%u) with free entries.\n",
 		       blk);
 	return 0;
@@ -244,7 +252,7 @@
 	if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
 		*err = remove_free_dqentry(info, buf, blk);
 		if (*err < 0) {
-			printk(KERN_ERR "VFS: find_free_dqentry(): Can't "
+			q_warn(KERN_ERR "VFS: find_free_dqentry(): Can't "
 			       "remove block (%u) from entry free list.\n",
 			       blk);
 			goto out_buf;
@@ -268,7 +276,7 @@
 #endif
 	*err = write_blk(info, blk, buf);
 	if (*err < 0) {
-		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota "
+		q_warn(KERN_ERR "VFS: find_free_dqentry(): Can't write quota "
 				"data block %u.\n", blk);
 		goto out_buf;
 	}
@@ -303,7 +311,7 @@
 	} else {
 		ret = read_blk(info, *treeblk, buf);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: Can't read tree quota block "
+			q_warn(KERN_ERR "VFS: Can't read tree quota block "
 					"%u.\n", *treeblk);
 			goto out_buf;
 		}
@@ -365,7 +373,7 @@
 	if (!dquot->dq_off) {
 		ret = dq_insert_tree(info, dquot);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: Error %zd occurred while "
+			q_warn(KERN_ERR "VFS: Error %zd occurred while "
 					"creating quota.\n", ret);
 			kfree(ddquot);
 			return ret;
@@ -377,7 +385,7 @@
 	ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
 				    dquot->dq_off);
 	if (ret != info->dqi_entry_size) {
-		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+		q_warn(KERN_WARNING "VFS: dquota write failed on dev %s\n",
 		       sb->s_id);
 		if (ret >= 0)
 			ret = -ENOSPC;
@@ -402,14 +410,14 @@
 	if (!buf)
 		return -ENOMEM;
 	if (dquot->dq_off >> info->dqi_blocksize_bits != blk) {
-		printk(KERN_ERR "VFS: Quota structure has offset to other "
+		q_warn(KERN_ERR "VFS: Quota structure has offset to other "
 		  "block (%u) than it should (%u).\n", blk,
 		  (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
 		goto out_buf;
 	}
 	ret = read_blk(info, blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+		q_warn(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
 		goto out_buf;
 	}
 	dh = (struct qt_disk_dqdbheader *)buf;
@@ -419,7 +427,7 @@
 		if (ret >= 0)
 			ret = put_free_dqblk(info, buf, blk);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: Can't move quota data block (%u) "
+			q_warn(KERN_ERR "VFS: Can't move quota data block (%u) "
 			  "to free list.\n", blk);
 			goto out_buf;
 		}
@@ -432,14 +440,14 @@
 			/* Insert will write block itself */
 			ret = insert_free_dqentry(info, buf, blk);
 			if (ret < 0) {
-				printk(KERN_ERR "VFS: Can't insert quota data "
+				q_warn(KERN_ERR "VFS: Can't insert quota data "
 				       "block (%u) to free entry list.\n", blk);
 				goto out_buf;
 			}
 		} else {
 			ret = write_blk(info, blk, buf);
 			if (ret < 0) {
-				printk(KERN_ERR "VFS: Can't write quota data "
+				q_warn(KERN_ERR "VFS: Can't write quota data "
 				  "block %u\n", blk);
 				goto out_buf;
 			}
@@ -464,7 +472,7 @@
 		return -ENOMEM;
 	ret = read_blk(info, *blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+		q_warn(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
 		goto out_buf;
 	}
 	newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
@@ -488,7 +496,7 @@
 		} else {
 			ret = write_blk(info, *blk, buf);
 			if (ret < 0)
-				printk(KERN_ERR "VFS: Can't write quota tree "
+				q_warn(KERN_ERR "VFS: Can't write quota tree "
 				  "block %u.\n", *blk);
 		}
 	}
@@ -521,7 +529,7 @@
 		return -ENOMEM;
 	ret = read_blk(info, blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		q_warn(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
 		goto out_buf;
 	}
 	ddquot = buf + sizeof(struct qt_disk_dqdbheader);
@@ -531,7 +539,7 @@
 		ddquot += info->dqi_entry_size;
 	}
 	if (i == qtree_dqstr_in_blk(info)) {
-		printk(KERN_ERR "VFS: Quota for id %u referenced "
+		q_warn(KERN_ERR "VFS: Quota for id %u referenced "
 		  "but not present.\n", dquot->dq_id);
 		ret = -EIO;
 		goto out_buf;
@@ -556,7 +564,7 @@
 		return -ENOMEM;
 	ret = read_blk(info, blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		q_warn(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
 		goto out_buf;
 	}
 	ret = 0;
@@ -599,7 +607,7 @@
 		offset = find_dqentry(info, dquot);
 		if (offset <= 0) {	/* Entry not present? */
 			if (offset < 0)
-				printk(KERN_ERR "VFS: Can't read quota "
+				q_warn(KERN_ERR "VFS: Can't read quota "
 				  "structure for id %u.\n", dquot->dq_id);
 			dquot->dq_off = 0;
 			set_bit(DQ_FAKE_B, &dquot->dq_flags);
@@ -617,7 +625,7 @@
 	if (ret != info->dqi_entry_size) {
 		if (ret >= 0)
 			ret = -EIO;
-		printk(KERN_ERR "VFS: Error while reading quota "
+		q_warn(KERN_ERR "VFS: Error while reading quota "
 				"structure for id %u.\n", dquot->dq_id);
 		set_bit(DQ_FAKE_B, &dquot->dq_flags);
 		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
diff --git a/fs/quota/quota_tree.h b/fs/quota/quota_tree.h
index a1ab8db..ccc3e71 100644
--- a/fs/quota/quota_tree.h
+++ b/fs/quota/quota_tree.h
@@ -22,4 +22,10 @@
 
 #define QT_TREEOFF	1		/* Offset of tree in file in blocks */
 
+#define q_warn(fmt, args...) \
+do { \
+	if (printk_ratelimit()) \
+		printk(fmt, ## args); \
+} while(0)
+
 #endif /* _LINUX_QUOTAIO_TREE_H */
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index e3da02f4..135206a 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -63,7 +63,7 @@
 	size = sb->s_op->quota_read(sb, type, (char *)dqhead,
 				    sizeof(struct v2_disk_dqheader), 0);
 	if (size != sizeof(struct v2_disk_dqheader)) {
-		printk(KERN_WARNING "quota_v2: Failed header read:"
+		q_warn(KERN_WARNING "quota_v2: Failed header read:"
 		       " expected=%zd got=%zd\n",
 			sizeof(struct v2_disk_dqheader), size);
 		return 0;
@@ -106,7 +106,7 @@
 	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
-		printk(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n",
+		q_warn(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n",
 			sb->s_id);
 		return -1;
 	}
@@ -167,7 +167,7 @@
 	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
-		printk(KERN_WARNING "Can't write info structure on device %s.\n",
+		q_warn(KERN_WARNING "Can't write info structure on device %s.\n",
 			sb->s_id);
 		return -1;
 	}