ext4: add ext4_sb_bread() to disambiguate ENOMEM cases
Today, when sb_bread() returns NULL, this can either be because of an
I/O error or because the system failed to allocate the buffer. Since
it's an old interface, changing would require changing many call
sites.
So instead we create our own ext4_sb_bread(), which also allows us to
set the REQ_META flag.
Also fixed a problem in the xattr code where a NULL return in a
function could also mean that the xattr was not found, which could
lead to the wrong error getting returned to userspace.
Fixes: ac27a0ec11
("ext4: initial copy of files from ext3")
Cc: stable@kernel.org # 2.6.19
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
@@ -127,10 +127,12 @@ static int verify_group_input(struct super_block *sb,
|
||||
else if (free_blocks_count < 0)
|
||||
ext4_warning(sb, "Bad blocks count %u",
|
||||
input->blocks_count);
|
||||
else if (!(bh = sb_bread(sb, end - 1)))
|
||||
else if (IS_ERR(bh = ext4_sb_bread(sb, end - 1, 0))) {
|
||||
err = PTR_ERR(bh);
|
||||
bh = NULL;
|
||||
ext4_warning(sb, "Cannot read last block (%llu)",
|
||||
end - 1);
|
||||
else if (outside(input->block_bitmap, start, end))
|
||||
} else if (outside(input->block_bitmap, start, end))
|
||||
ext4_warning(sb, "Block bitmap not in group (block %llu)",
|
||||
(unsigned long long)input->block_bitmap);
|
||||
else if (outside(input->inode_bitmap, start, end))
|
||||
@@ -781,11 +783,11 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
|
||||
unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb);
|
||||
ext4_fsblk_t gdblock = EXT4_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
|
||||
struct buffer_head **o_group_desc, **n_group_desc;
|
||||
struct buffer_head *dind;
|
||||
struct buffer_head *gdb_bh;
|
||||
struct buffer_head **o_group_desc, **n_group_desc = NULL;
|
||||
struct buffer_head *dind = NULL;
|
||||
struct buffer_head *gdb_bh = NULL;
|
||||
int gdbackups;
|
||||
struct ext4_iloc iloc;
|
||||
struct ext4_iloc iloc = { .bh = NULL };
|
||||
__le32 *data;
|
||||
int err;
|
||||
|
||||
@@ -794,21 +796,22 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
"EXT4-fs: ext4_add_new_gdb: adding group block %lu\n",
|
||||
gdb_num);
|
||||
|
||||
gdb_bh = sb_bread(sb, gdblock);
|
||||
if (!gdb_bh)
|
||||
return -EIO;
|
||||
gdb_bh = ext4_sb_bread(sb, gdblock, 0);
|
||||
if (IS_ERR(gdb_bh))
|
||||
return PTR_ERR(gdb_bh);
|
||||
|
||||
gdbackups = verify_reserved_gdb(sb, group, gdb_bh);
|
||||
if (gdbackups < 0) {
|
||||
err = gdbackups;
|
||||
goto exit_bh;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
data = EXT4_I(inode)->i_data + EXT4_DIND_BLOCK;
|
||||
dind = sb_bread(sb, le32_to_cpu(*data));
|
||||
if (!dind) {
|
||||
err = -EIO;
|
||||
goto exit_bh;
|
||||
dind = ext4_sb_bread(sb, le32_to_cpu(*data), 0);
|
||||
if (IS_ERR(dind)) {
|
||||
err = PTR_ERR(dind);
|
||||
dind = NULL;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
data = (__le32 *)dind->b_data;
|
||||
@@ -816,18 +819,18 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
ext4_warning(sb, "new group %u GDT block %llu not reserved",
|
||||
group, gdblock);
|
||||
err = -EINVAL;
|
||||
goto exit_dind;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
|
||||
if (unlikely(err))
|
||||
goto exit_dind;
|
||||
goto errout;
|
||||
|
||||
BUFFER_TRACE(gdb_bh, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, gdb_bh);
|
||||
if (unlikely(err))
|
||||
goto exit_dind;
|
||||
goto errout;
|
||||
|
||||
BUFFER_TRACE(dind, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, dind);
|
||||
@@ -837,7 +840,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
/* ext4_reserve_inode_write() gets a reference on the iloc */
|
||||
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
||||
if (unlikely(err))
|
||||
goto exit_dind;
|
||||
goto errout;
|
||||
|
||||
n_group_desc = ext4_kvmalloc((gdb_num + 1) *
|
||||
sizeof(struct buffer_head *),
|
||||
@@ -846,7 +849,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
err = -ENOMEM;
|
||||
ext4_warning(sb, "not enough memory for %lu groups",
|
||||
gdb_num + 1);
|
||||
goto exit_inode;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -862,7 +865,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, dind);
|
||||
if (unlikely(err)) {
|
||||
ext4_std_error(sb, err);
|
||||
goto exit_inode;
|
||||
goto errout;
|
||||
}
|
||||
inode->i_blocks -= (gdbackups + 1) * sb->s_blocksize >>
|
||||
(9 - EXT4_SB(sb)->s_cluster_bits);
|
||||
@@ -871,8 +874,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
|
||||
if (unlikely(err)) {
|
||||
ext4_std_error(sb, err);
|
||||
iloc.bh = NULL;
|
||||
goto exit_inode;
|
||||
goto errout;
|
||||
}
|
||||
brelse(dind);
|
||||
|
||||
@@ -888,15 +890,11 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
|
||||
err = ext4_handle_dirty_super(handle, sb);
|
||||
if (err)
|
||||
ext4_std_error(sb, err);
|
||||
|
||||
return err;
|
||||
|
||||
exit_inode:
|
||||
errout:
|
||||
kvfree(n_group_desc);
|
||||
brelse(iloc.bh);
|
||||
exit_dind:
|
||||
brelse(dind);
|
||||
exit_bh:
|
||||
brelse(gdb_bh);
|
||||
|
||||
ext4_debug("leaving with error %d\n", err);
|
||||
@@ -916,9 +914,9 @@ static int add_new_gdb_meta_bg(struct super_block *sb,
|
||||
|
||||
gdblock = ext4_meta_bg_first_block_no(sb, group) +
|
||||
ext4_bg_has_super(sb, group);
|
||||
gdb_bh = sb_bread(sb, gdblock);
|
||||
if (!gdb_bh)
|
||||
return -EIO;
|
||||
gdb_bh = ext4_sb_bread(sb, gdblock, 0);
|
||||
if (IS_ERR(gdb_bh))
|
||||
return PTR_ERR(gdb_bh);
|
||||
n_group_desc = ext4_kvmalloc((gdb_num + 1) *
|
||||
sizeof(struct buffer_head *),
|
||||
GFP_NOFS);
|
||||
@@ -975,9 +973,10 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
|
||||
return -ENOMEM;
|
||||
|
||||
data = EXT4_I(inode)->i_data + EXT4_DIND_BLOCK;
|
||||
dind = sb_bread(sb, le32_to_cpu(*data));
|
||||
if (!dind) {
|
||||
err = -EIO;
|
||||
dind = ext4_sb_bread(sb, le32_to_cpu(*data), 0);
|
||||
if (IS_ERR(dind)) {
|
||||
err = PTR_ERR(dind);
|
||||
dind = NULL;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
@@ -996,9 +995,10 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
|
||||
err = -EINVAL;
|
||||
goto exit_bh;
|
||||
}
|
||||
primary[res] = sb_bread(sb, blk);
|
||||
if (!primary[res]) {
|
||||
err = -EIO;
|
||||
primary[res] = ext4_sb_bread(sb, blk, 0);
|
||||
if (IS_ERR(primary[res])) {
|
||||
err = PTR_ERR(primary[res]);
|
||||
primary[res] = NULL;
|
||||
goto exit_bh;
|
||||
}
|
||||
gdbackups = verify_reserved_gdb(sb, group, primary[res]);
|
||||
|
Reference in New Issue
Block a user