Skip to content

Commit

Permalink
ext4: add ext4_sb_bread() to disambiguate ENOMEM cases
Browse files Browse the repository at this point in the history
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: ac27a0e ("ext4: initial copy of files from ext3")
Cc: stable@kernel.org # 2.6.19
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Theodore Ts'o committed Nov 25, 2018
1 parent 2e6e902 commit fb265c9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 94 deletions.
2 changes: 2 additions & 0 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -2538,6 +2538,8 @@ extern int ext4_group_extend(struct super_block *sb,
extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);

/* super.c */
extern struct buffer_head *ext4_sb_bread(struct super_block *sb,
sector_t block, int op_flags);
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
extern int ext4_calculate_overhead(struct super_block *sb);
extern void ext4_superblock_csum_set(struct super_block *sb);
Expand Down
36 changes: 18 additions & 18 deletions fs/ext4/migrate.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ static int update_ind_extent_range(handle_t *handle, struct inode *inode,
int i, retval = 0;
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;

bh = sb_bread(inode->i_sb, pblock);
if (!bh)
return -EIO;
bh = ext4_sb_bread(inode->i_sb, pblock, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);

i_data = (__le32 *)bh->b_data;
for (i = 0; i < max_entries; i++) {
Expand All @@ -145,9 +145,9 @@ static int update_dind_extent_range(handle_t *handle, struct inode *inode,
int i, retval = 0;
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;

bh = sb_bread(inode->i_sb, pblock);
if (!bh)
return -EIO;
bh = ext4_sb_bread(inode->i_sb, pblock, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);

i_data = (__le32 *)bh->b_data;
for (i = 0; i < max_entries; i++) {
Expand Down Expand Up @@ -175,9 +175,9 @@ static int update_tind_extent_range(handle_t *handle, struct inode *inode,
int i, retval = 0;
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;

bh = sb_bread(inode->i_sb, pblock);
if (!bh)
return -EIO;
bh = ext4_sb_bread(inode->i_sb, pblock, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);

i_data = (__le32 *)bh->b_data;
for (i = 0; i < max_entries; i++) {
Expand Down Expand Up @@ -224,9 +224,9 @@ static int free_dind_blocks(handle_t *handle,
struct buffer_head *bh;
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;

bh = sb_bread(inode->i_sb, le32_to_cpu(i_data));
if (!bh)
return -EIO;
bh = ext4_sb_bread(inode->i_sb, le32_to_cpu(i_data), 0);
if (IS_ERR(bh))
return PTR_ERR(bh);

tmp_idata = (__le32 *)bh->b_data;
for (i = 0; i < max_entries; i++) {
Expand Down Expand Up @@ -254,9 +254,9 @@ static int free_tind_blocks(handle_t *handle,
struct buffer_head *bh;
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;

bh = sb_bread(inode->i_sb, le32_to_cpu(i_data));
if (!bh)
return -EIO;
bh = ext4_sb_bread(inode->i_sb, le32_to_cpu(i_data), 0);
if (IS_ERR(bh))
return PTR_ERR(bh);

tmp_idata = (__le32 *)bh->b_data;
for (i = 0; i < max_entries; i++) {
Expand Down Expand Up @@ -382,9 +382,9 @@ static int free_ext_idx(handle_t *handle, struct inode *inode,
struct ext4_extent_header *eh;

block = ext4_idx_pblock(ix);
bh = sb_bread(inode->i_sb, block);
if (!bh)
return -EIO;
bh = ext4_sb_bread(inode->i_sb, block, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);

eh = (struct ext4_extent_header *)bh->b_data;
if (eh->eh_depth != 0) {
Expand Down
72 changes: 36 additions & 36 deletions fs/ext4/resize.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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;

Expand All @@ -794,40 +796,41 @@ 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;
if (le32_to_cpu(data[gdb_num % EXT4_ADDR_PER_BLOCK(sb)]) != gdblock) {
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);
Expand All @@ -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 *),
Expand All @@ -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;
}

/*
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -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]);
Expand Down
23 changes: 23 additions & 0 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,29 @@ MODULE_ALIAS_FS("ext3");
MODULE_ALIAS("ext3");
#define IS_EXT3_SB(sb) ((sb)->s_bdev->bd_holder == &ext3_fs_type)

/*
* This works like sb_bread() except it uses ERR_PTR for error
* returns. Currently with sb_bread it's impossible to distinguish
* between ENOMEM and EIO situations (since both result in a NULL
* return.
*/
struct buffer_head *
ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags)
{
struct buffer_head *bh = sb_getblk(sb, block);

if (bh == NULL)
return ERR_PTR(-ENOMEM);
if (buffer_uptodate(bh))
return bh;
ll_rw_block(REQ_OP_READ, REQ_META | op_flags, 1, &bh);
wait_on_buffer(bh);
if (buffer_uptodate(bh))
return bh;
put_bh(bh);
return ERR_PTR(-EIO);
}

static int ext4_verify_csum_type(struct super_block *sb,
struct ext4_super_block *es)
{
Expand Down
Loading

0 comments on commit fb265c9

Please sign in to comment.