@@ -221,6 +221,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
struct ext4_super_block *es;
struct ext4_sb_info *sbi;
int fatal = 0, err, count, cleared;
+ int uninit = 0;
if (!sb) {
printk(KERN_ERR "EXT4-fs: %s:%d: inode on "
@@ -266,36 +267,65 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb);
bitmap_bh = ext4_read_inode_bitmap(sb, block_group);
- if (!bitmap_bh)
- goto error_return;
- BUFFER_TRACE(bitmap_bh, "get_write_access");
- fatal = ext4_journal_get_write_access(handle, bitmap_bh);
- if (fatal)
+ if (!bitmap_bh)
goto error_return;
- fatal = -ESRCH;
gdp = ext4_get_group_desc(sb, block_group, &bh2);
if (gdp) {
BUFFER_TRACE(bh2, "get_write_access");
fatal = ext4_journal_get_write_access(handle, bh2);
}
+
+ if(fatal)
+ goto error_return;
+
ext4_lock_group(sb, block_group);
- cleared = ext4_test_and_clear_bit(bit, bitmap_bh->b_data);
- if (fatal || !cleared) {
+ count = ext4_free_inodes_count(sb, gdp) + 1;
+
+ if(ext4_has_group_desc_csum(sb) && count == EXT4_INODES_PER_GROUP(sb)) {
+ unsigned long first_bit = ext4_find_next_bit(bitmap_bh->b_data,\
+ EXT4_INODES_PER_GROUP(sb), 0);
+ unsigned long next_bit = ext4_find_next_bit(bitmap_bh->b_data,\
+ EXT4_INODES_PER_GROUP(sb), bit + 1);
+ if(first_bit != bit || next_bit < EXT4_INODES_PER_GROUP(sb)) {
+ ext4_unlock_group(sb, block_group);
+ ext4_error(sb, "Inode bitmap corruption in block_group %u",\
+ block_group);
+ goto error_return;
+ }
+
+ gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_UNINIT);
+ uninit = 1;
+ }
+ else {
ext4_unlock_group(sb, block_group);
- goto out;
+
+ BUFFER_TRACE(bitmap_bh, "get_write_access");
+ fatal = ext4_journal_get_write_access(handle, bitmap_bh);
+
+ if(fatal)
+ goto error_return;
+
+ ext4_lock_group(sb, block_group);
+ count = ext4_free_inodes_count(sb, gdp) + 1;
+
+ cleared = ext4_test_and_clear_bit(bit, bitmap_bh->b_data);
+ if(!cleared) {
+ ext4_unlock_group(sb, block_group);
+ goto out;
+ }
+ ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
+ EXT4_INODES_PER_GROUP(sb) / 8);
}
- count = ext4_free_inodes_count(sb, gdp) + 1;
ext4_free_inodes_set(sb, gdp, count);
+
if (is_directory) {
count = ext4_used_dirs_count(sb, gdp) - 1;
ext4_used_dirs_set(sb, gdp, count);
percpu_counter_dec(&sbi->s_dirs_counter);
}
- ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
- EXT4_INODES_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, block_group, gdp);
ext4_unlock_group(sb, block_group);
@@ -310,13 +340,20 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
BUFFER_TRACE(bh2, "call ext4_handle_dirty_metadata");
fatal = ext4_handle_dirty_metadata(handle, NULL, bh2);
out:
- if (cleared) {
- BUFFER_TRACE(bitmap_bh, "call ext4_handle_dirty_metadata");
- err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
- if (!fatal)
- fatal = err;
- } else
- ext4_error(sb, "bit already cleared for inode %lu", ino);
+ if(!uninit) {
+ if (cleared) {
+ BUFFER_TRACE(bitmap_bh, "call ext4_handle_dirty_metadata");
+ err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
+ if (!fatal)
+ fatal = err;
+ } else
+ ext4_error(sb, "bit already cleared for inode %lu", ino);
+ } else {
+ ext4_fsblk_t bitmap_blk = ext4_inode_bitmap(sb, gdp);
+ fatal = ext4_forget(handle, 0, inode, bitmap_bh, bitmap_blk);
+ ext4_std_error(sb, fatal);
+ return;
+ }
error_return:
brelse(bitmap_bh);
@@ -650,6 +687,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
struct inode *ret;
ext4_group_t i;
ext4_group_t flex_group;
+ int free = -1;
/* Cannot create files in a deleted directory */
if (!dir || !dir->i_nlink)
@@ -730,7 +768,20 @@ repeat_in_this_group:
if (err)
goto fail;
ext4_lock_group(sb, group);
- ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
+ if(ext4_has_group_desc_csum(sb) &&
+ gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
+
+ gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
+ ext4_group_desc_csum_set(sb, group, gdp);
+ memset(inode_bitmap_bh->b_data, 0, sb->s_blocksize);
+ ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb),\
+ sb->s_blocksize * 8, inode_bitmap_bh->b_data);
+ ext4_set_bit(ino, inode_bitmap_bh->b_data);
+ free = 0;
+ ret2 = 0;
+ }
+ else
+ ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
ext4_unlock_group(sb, group);
ino++; /* the inode bitmap is zero-based */
if (!ret2)
@@ -787,17 +838,12 @@ got:
/* Update the relevant bg descriptor fields */
if (ext4_has_group_desc_csum(sb)) {
- int free;
struct ext4_group_info *grp = ext4_get_group_info(sb, group);
down_read(&grp->alloc_sem); /* protect vs itable lazyinit */
ext4_lock_group(sb, group); /* while we modify the bg desc */
- free = EXT4_INODES_PER_GROUP(sb) -
- ext4_itable_unused_count(sb, gdp);
- if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
- free = 0;
- }
+ if(free != 0)
+ free = EXT4_INODES_PER_GROUP(sb) - ext4_itable_unused_count(sb, gdp);
/*
* Check the relative inode number against the last used
* relative inode number in this group. if it is greater