Commit 1907131b authored by Jiaying Zhang's avatar Jiaying Zhang Committed by Jan Kara

dquot: Detect partial write error to quota file in write_blk() and add...

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: default avatarJiaying Zhang <jiayingz@google.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent c06bcbfa
...@@ -60,9 +60,17 @@ static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) ...@@ -60,9 +60,17 @@ static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
{ {
struct super_block *sb = info->dqi_sb; 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); 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 */ /* Remove empty block from list and return it */
...@@ -152,7 +160,7 @@ static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, ...@@ -152,7 +160,7 @@ static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
/* No matter whether write succeeds block is out of list */ /* No matter whether write succeeds block is out of list */
if (write_blk(info, blk, buf) < 0) if (write_blk(info, blk, buf) < 0)
printk(KERN_ERR q_warn(KERN_ERR
"VFS: Can't write block (%u) with free entries.\n", "VFS: Can't write block (%u) with free entries.\n",
blk); blk);
return 0; return 0;
...@@ -244,7 +252,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info, ...@@ -244,7 +252,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) { if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
*err = remove_free_dqentry(info, buf, blk); *err = remove_free_dqentry(info, buf, blk);
if (*err < 0) { 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", "remove block (%u) from entry free list.\n",
blk); blk);
goto out_buf; goto out_buf;
...@@ -268,7 +276,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info, ...@@ -268,7 +276,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
#endif #endif
*err = write_blk(info, blk, buf); *err = write_blk(info, blk, buf);
if (*err < 0) { 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); "data block %u.\n", blk);
goto out_buf; goto out_buf;
} }
...@@ -303,7 +311,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, ...@@ -303,7 +311,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
} else { } else {
ret = read_blk(info, *treeblk, buf); ret = read_blk(info, *treeblk, buf);
if (ret < 0) { 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); "%u.\n", *treeblk);
goto out_buf; goto out_buf;
} }
...@@ -365,7 +373,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -365,7 +373,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
if (!dquot->dq_off) { if (!dquot->dq_off) {
ret = dq_insert_tree(info, dquot); ret = dq_insert_tree(info, dquot);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "VFS: Error %zd occurred while " q_warn(KERN_ERR "VFS: Error %zd occurred while "
"creating quota.\n", ret); "creating quota.\n", ret);
kfree(ddquot); kfree(ddquot);
return ret; return ret;
...@@ -377,7 +385,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -377,7 +385,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size, ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
dquot->dq_off); dquot->dq_off);
if (ret != info->dqi_entry_size) { 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); sb->s_id);
if (ret >= 0) if (ret >= 0)
ret = -ENOSPC; ret = -ENOSPC;
...@@ -402,14 +410,14 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, ...@@ -402,14 +410,14 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { 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, "block (%u) than it should (%u).\n", blk,
(uint)(dquot->dq_off >> info->dqi_blocksize_bits)); (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
goto out_buf; goto out_buf;
} }
ret = read_blk(info, blk, buf); ret = read_blk(info, blk, buf);
if (ret < 0) { 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; goto out_buf;
} }
dh = (struct qt_disk_dqdbheader *)buf; dh = (struct qt_disk_dqdbheader *)buf;
...@@ -419,7 +427,7 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, ...@@ -419,7 +427,7 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
if (ret >= 0) if (ret >= 0)
ret = put_free_dqblk(info, buf, blk); ret = put_free_dqblk(info, buf, blk);
if (ret < 0) { 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); "to free list.\n", blk);
goto out_buf; goto out_buf;
} }
...@@ -432,14 +440,14 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, ...@@ -432,14 +440,14 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
/* Insert will write block itself */ /* Insert will write block itself */
ret = insert_free_dqentry(info, buf, blk); ret = insert_free_dqentry(info, buf, blk);
if (ret < 0) { 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); "block (%u) to free entry list.\n", blk);
goto out_buf; goto out_buf;
} }
} else { } else {
ret = write_blk(info, blk, buf); ret = write_blk(info, blk, buf);
if (ret < 0) { 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); "block %u\n", blk);
goto out_buf; goto out_buf;
} }
...@@ -464,7 +472,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, ...@@ -464,7 +472,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
return -ENOMEM; return -ENOMEM;
ret = read_blk(info, *blk, buf); ret = read_blk(info, *blk, buf);
if (ret < 0) { 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; goto out_buf;
} }
newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
...@@ -488,7 +496,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, ...@@ -488,7 +496,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
} else { } else {
ret = write_blk(info, *blk, buf); ret = write_blk(info, *blk, buf);
if (ret < 0) 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); "block %u.\n", *blk);
} }
} }
...@@ -521,7 +529,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, ...@@ -521,7 +529,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
return -ENOMEM; return -ENOMEM;
ret = read_blk(info, blk, buf); ret = read_blk(info, blk, buf);
if (ret < 0) { 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; goto out_buf;
} }
ddquot = buf + sizeof(struct qt_disk_dqdbheader); ddquot = buf + sizeof(struct qt_disk_dqdbheader);
...@@ -531,7 +539,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, ...@@ -531,7 +539,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
ddquot += info->dqi_entry_size; ddquot += info->dqi_entry_size;
} }
if (i == qtree_dqstr_in_blk(info)) { 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); "but not present.\n", dquot->dq_id);
ret = -EIO; ret = -EIO;
goto out_buf; goto out_buf;
...@@ -556,7 +564,7 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info, ...@@ -556,7 +564,7 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
return -ENOMEM; return -ENOMEM;
ret = read_blk(info, blk, buf); ret = read_blk(info, blk, buf);
if (ret < 0) { 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; goto out_buf;
} }
ret = 0; ret = 0;
...@@ -599,7 +607,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -599,7 +607,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
offset = find_dqentry(info, dquot); offset = find_dqentry(info, dquot);
if (offset <= 0) { /* Entry not present? */ if (offset <= 0) { /* Entry not present? */
if (offset < 0) 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); "structure for id %u.\n", dquot->dq_id);
dquot->dq_off = 0; dquot->dq_off = 0;
set_bit(DQ_FAKE_B, &dquot->dq_flags); set_bit(DQ_FAKE_B, &dquot->dq_flags);
...@@ -617,7 +625,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -617,7 +625,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
if (ret != info->dqi_entry_size) { if (ret != info->dqi_entry_size) {
if (ret >= 0) if (ret >= 0)
ret = -EIO; 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); "structure for id %u.\n", dquot->dq_id);
set_bit(DQ_FAKE_B, &dquot->dq_flags); set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
......
...@@ -22,4 +22,10 @@ struct qt_disk_dqdbheader { ...@@ -22,4 +22,10 @@ struct qt_disk_dqdbheader {
#define QT_TREEOFF 1 /* Offset of tree in file in blocks */ #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 */ #endif /* _LINUX_QUOTAIO_TREE_H */
...@@ -63,7 +63,7 @@ static int v2_read_header(struct super_block *sb, int type, ...@@ -63,7 +63,7 @@ static int v2_read_header(struct super_block *sb, int type,
size = sb->s_op->quota_read(sb, type, (char *)dqhead, size = sb->s_op->quota_read(sb, type, (char *)dqhead,
sizeof(struct v2_disk_dqheader), 0); sizeof(struct v2_disk_dqheader), 0);
if (size != sizeof(struct v2_disk_dqheader)) { 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", " expected=%zd got=%zd\n",
sizeof(struct v2_disk_dqheader), size); sizeof(struct v2_disk_dqheader), size);
return 0; return 0;
...@@ -106,7 +106,7 @@ static int v2_read_file_info(struct super_block *sb, int type) ...@@ -106,7 +106,7 @@ static int v2_read_file_info(struct super_block *sb, int type)
size = sb->s_op->quota_read(sb, type, (char *)&dinfo, size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
if (size != sizeof(struct v2_disk_dqinfo)) { 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); sb->s_id);
return -1; return -1;
} }
...@@ -167,7 +167,7 @@ static int v2_write_file_info(struct super_block *sb, int type) ...@@ -167,7 +167,7 @@ static int v2_write_file_info(struct super_block *sb, int type)
size = sb->s_op->quota_write(sb, type, (char *)&dinfo, size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
if (size != sizeof(struct v2_disk_dqinfo)) { 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); sb->s_id);
return -1; return -1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment