forked from Minki/linux
A lot of cleanups and bug fixes, especially dealing with corrupted
file systems. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAlsWmXcACgkQ8vlZVpUN gaMGmAf+JGK4XysAvlJuj9tJfFPHHwgXSBBe/GAgyjhW9XhtNRHprUM2SpvwpIdI Isl5O8Ec+FywBJB0I4AGy6yds6DE6jn38FFRFEhVmkp4EoROJiIr8+a7spfVuC3m cWrHBgc7FwK4qYlyuGtH2+6NYva+KNFr+wwbvvUusvldyZAWMzflfrcdHM6D+/JE +Sd5I7aniqnP5fICq0b4xrP2zWO4XJEKMbZO2dJ9yRtMmUnbaSj6G+bTGDRyfrNk L3wJhqIu93o7zjDaEC0UfXSLAXzoDGWHeq7fBssaJiXj/hNtAvAGPaRMbgFR9a3h uHmhvf84iyJuM+8GgG25UqeGwCuWiA== =b0VQ -----END PGP SIGNATURE----- Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: "A lot of cleanups and bug fixes, especially dealing with corrupted file systems" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (23 commits) ext4: fix fencepost error in check for inode count overflow during resize ext4: correctly handle a zero-length xattr with a non-zero e_value_offs ext4: bubble errors from ext4_find_inline_data_nolock() up to ext4_iget() ext4: do not allow external inodes for inline data ext4: report delalloc reserve as non-free in statfs for project quota ext4: remove NULL check before calling kmem_cache_destroy() jbd2: remove NULL check before calling kmem_cache_destroy() jbd2: remove bunch of empty lines with jbd2 debug ext4: handle errors on ext4_commit_super ext4: do not update s_last_mounted of a frozen fs ext4: factor out helper ext4_sample_last_mounted() vfs: add the sb_start_intwrite_trylock() helper ext4: update mtime in ext4_punch_hole even if no blocks are released ext4: add verifier check for symlink with append/immutable flags fs: ext4: add new return type vm_fault_t ext4: fix hole length detection in ext4_ind_map_blocks() ext4: mark block bitmap corrupted when found ext4: mark inode bitmap corrupted when found ext4: add new ext4_mark_group_bitmap_corrupted() helper ext4: fix wrong return value in ext4_read_inode_bitmap() ...
This commit is contained in:
commit
1434763ca5
@ -185,25 +185,15 @@ static int ext4_init_block_bitmap(struct super_block *sb,
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
ext4_fsblk_t start, tmp;
|
||||
int flex_bg = 0;
|
||||
struct ext4_group_info *grp;
|
||||
|
||||
J_ASSERT_BH(bh, buffer_locked(bh));
|
||||
|
||||
/* If checksum is bad mark all blocks used to prevent allocation
|
||||
* essentially implementing a per-group read-only flag. */
|
||||
if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
|
||||
grp = ext4_get_group_info(sb, block_group);
|
||||
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter,
|
||||
grp->bb_free);
|
||||
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
|
||||
int count;
|
||||
count = ext4_free_inodes_count(sb, gdp);
|
||||
percpu_counter_sub(&sbi->s_freeinodes_counter,
|
||||
count);
|
||||
}
|
||||
set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT |
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
return -EFSBADCRC;
|
||||
}
|
||||
memset(bh->b_data, 0, sb->s_blocksize);
|
||||
@ -375,7 +365,6 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
|
||||
{
|
||||
ext4_fsblk_t blk;
|
||||
struct ext4_group_info *grp = ext4_get_group_info(sb, block_group);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
if (buffer_verified(bh))
|
||||
return 0;
|
||||
@ -387,10 +376,8 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
|
||||
desc, bh))) {
|
||||
ext4_unlock_group(sb, block_group);
|
||||
ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
|
||||
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter,
|
||||
grp->bb_free);
|
||||
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
return -EFSBADCRC;
|
||||
}
|
||||
blk = ext4_valid_block_bitmap(sb, desc, block_group, bh);
|
||||
@ -398,10 +385,8 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
|
||||
ext4_unlock_group(sb, block_group);
|
||||
ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
|
||||
block_group, blk);
|
||||
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter,
|
||||
grp->bb_free);
|
||||
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
set_buffer_verified(bh);
|
||||
@ -436,6 +421,8 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
|
||||
(bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
|
||||
ext4_error(sb, "Invalid block bitmap block %llu in "
|
||||
"block_group %u", bitmap_blk, block_group);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
}
|
||||
bh = sb_getblk(sb, bitmap_blk);
|
||||
@ -514,6 +501,8 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
|
||||
ext4_error(sb, "Cannot read block bitmap - "
|
||||
"block_group = %u, block_bitmap = %llu",
|
||||
block_group, (unsigned long long) bh->b_blocknr);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
return -EIO;
|
||||
}
|
||||
clear_buffer_new(bh);
|
||||
|
@ -2530,6 +2530,9 @@ extern int ext4_alloc_flex_bg_array(struct super_block *sb,
|
||||
ext4_group_t ngroup);
|
||||
extern const char *ext4_decode_error(struct super_block *sb, int errno,
|
||||
char nbuf[16]);
|
||||
extern void ext4_mark_group_bitmap_corrupted(struct super_block *sb,
|
||||
ext4_group_t block_group,
|
||||
unsigned int flags);
|
||||
|
||||
extern __printf(4, 5)
|
||||
void __ext4_error(struct super_block *, const char *, unsigned int,
|
||||
@ -2857,6 +2860,10 @@ struct ext4_group_info {
|
||||
#define EXT4_GROUP_INFO_WAS_TRIMMED_BIT 1
|
||||
#define EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT 2
|
||||
#define EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT 3
|
||||
#define EXT4_GROUP_INFO_BBITMAP_CORRUPT \
|
||||
(1 << EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT)
|
||||
#define EXT4_GROUP_INFO_IBITMAP_CORRUPT \
|
||||
(1 << EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT)
|
||||
|
||||
#define EXT4_MB_GRP_NEED_INIT(grp) \
|
||||
(test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state)))
|
||||
|
@ -162,8 +162,7 @@ int __init ext4_init_es(void)
|
||||
|
||||
void ext4_exit_es(void)
|
||||
{
|
||||
if (ext4_es_cachep)
|
||||
kmem_cache_destroy(ext4_es_cachep);
|
||||
kmem_cache_destroy(ext4_es_cachep);
|
||||
}
|
||||
|
||||
void ext4_es_init_tree(struct ext4_es_tree *tree)
|
||||
|
@ -277,10 +277,11 @@ out:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FS_DAX
|
||||
static int ext4_dax_huge_fault(struct vm_fault *vmf,
|
||||
static vm_fault_t ext4_dax_huge_fault(struct vm_fault *vmf,
|
||||
enum page_entry_size pe_size)
|
||||
{
|
||||
int result, error = 0;
|
||||
int error = 0;
|
||||
vm_fault_t result;
|
||||
int retries = 0;
|
||||
handle_t *handle = NULL;
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
@ -335,7 +336,7 @@ retry:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ext4_dax_fault(struct vm_fault *vmf)
|
||||
static vm_fault_t ext4_dax_fault(struct vm_fault *vmf)
|
||||
{
|
||||
return ext4_dax_huge_fault(vmf, PE_SIZE_PTE);
|
||||
}
|
||||
@ -380,50 +381,64 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext4_file_open(struct inode * inode, struct file * filp)
|
||||
static int ext4_sample_last_mounted(struct super_block *sb,
|
||||
struct vfsmount *mnt)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
struct vfsmount *mnt = filp->f_path.mnt;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
struct path path;
|
||||
char buf[64], *cp;
|
||||
handle_t *handle;
|
||||
int err;
|
||||
|
||||
if (likely(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED))
|
||||
return 0;
|
||||
|
||||
if (sb_rdonly(sb) || !sb_start_intwrite_trylock(sb))
|
||||
return 0;
|
||||
|
||||
sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED;
|
||||
/*
|
||||
* Sample where the filesystem has been mounted and
|
||||
* store it in the superblock for sysadmin convenience
|
||||
* when trying to sort through large numbers of block
|
||||
* devices or filesystem images.
|
||||
*/
|
||||
memset(buf, 0, sizeof(buf));
|
||||
path.mnt = mnt;
|
||||
path.dentry = mnt->mnt_root;
|
||||
cp = d_path(&path, buf, sizeof(buf));
|
||||
err = 0;
|
||||
if (IS_ERR(cp))
|
||||
goto out;
|
||||
|
||||
handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1);
|
||||
err = PTR_ERR(handle);
|
||||
if (IS_ERR(handle))
|
||||
goto out;
|
||||
BUFFER_TRACE(sbi->s_sbh, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
|
||||
if (err)
|
||||
goto out_journal;
|
||||
strlcpy(sbi->s_es->s_last_mounted, cp,
|
||||
sizeof(sbi->s_es->s_last_mounted));
|
||||
ext4_handle_dirty_super(handle, sb);
|
||||
out_journal:
|
||||
ext4_journal_stop(handle);
|
||||
out:
|
||||
sb_end_intwrite(sb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext4_file_open(struct inode * inode, struct file * filp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
|
||||
return -EIO;
|
||||
|
||||
if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
|
||||
!sb_rdonly(sb))) {
|
||||
sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED;
|
||||
/*
|
||||
* Sample where the filesystem has been mounted and
|
||||
* store it in the superblock for sysadmin convenience
|
||||
* when trying to sort through large numbers of block
|
||||
* devices or filesystem images.
|
||||
*/
|
||||
memset(buf, 0, sizeof(buf));
|
||||
path.mnt = mnt;
|
||||
path.dentry = mnt->mnt_root;
|
||||
cp = d_path(&path, buf, sizeof(buf));
|
||||
if (!IS_ERR(cp)) {
|
||||
handle_t *handle;
|
||||
int err;
|
||||
|
||||
handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
BUFFER_TRACE(sbi->s_sbh, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
|
||||
if (err) {
|
||||
ext4_journal_stop(handle);
|
||||
return err;
|
||||
}
|
||||
strlcpy(sbi->s_es->s_last_mounted, cp,
|
||||
sizeof(sbi->s_es->s_last_mounted));
|
||||
ext4_handle_dirty_super(handle, sb);
|
||||
ext4_journal_stop(handle);
|
||||
}
|
||||
}
|
||||
ret = ext4_sample_last_mounted(inode->i_sb, filp->f_path.mnt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fscrypt_file_open(inode, filp);
|
||||
if (ret)
|
||||
|
@ -402,8 +402,8 @@ static void ext4_getfsmap_free_fixed_metadata(struct list_head *meta_list)
|
||||
}
|
||||
|
||||
/* Find all the fixed metadata in the filesystem. */
|
||||
int ext4_getfsmap_find_fixed_metadata(struct super_block *sb,
|
||||
struct list_head *meta_list)
|
||||
static int ext4_getfsmap_find_fixed_metadata(struct super_block *sb,
|
||||
struct list_head *meta_list)
|
||||
{
|
||||
struct ext4_group_desc *gdp;
|
||||
ext4_group_t agno;
|
||||
|
@ -83,7 +83,6 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
|
||||
{
|
||||
ext4_fsblk_t blk;
|
||||
struct ext4_group_info *grp = ext4_get_group_info(sb, block_group);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
if (buffer_verified(bh))
|
||||
return 0;
|
||||
@ -97,14 +96,8 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
|
||||
ext4_unlock_group(sb, block_group);
|
||||
ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
|
||||
"inode_bitmap = %llu", block_group, blk);
|
||||
grp = ext4_get_group_info(sb, block_group);
|
||||
if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
|
||||
int count;
|
||||
count = ext4_free_inodes_count(sb, desc);
|
||||
percpu_counter_sub(&sbi->s_freeinodes_counter,
|
||||
count);
|
||||
}
|
||||
set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
return -EFSBADCRC;
|
||||
}
|
||||
set_buffer_verified(bh);
|
||||
@ -136,6 +129,8 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
|
||||
(bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
|
||||
ext4_error(sb, "Invalid inode bitmap blk %llu in "
|
||||
"block_group %u", bitmap_blk, block_group);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
}
|
||||
bh = sb_getblk(sb, bitmap_blk);
|
||||
@ -143,7 +138,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
|
||||
ext4_error(sb, "Cannot read inode bitmap - "
|
||||
"block_group = %u, inode_bitmap = %llu",
|
||||
block_group, bitmap_blk);
|
||||
return ERR_PTR(-EIO);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
if (bitmap_uptodate(bh))
|
||||
goto verify;
|
||||
@ -190,6 +185,8 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
|
||||
ext4_error(sb, "Cannot read inode bitmap - "
|
||||
"block_group = %u, inode_bitmap = %llu",
|
||||
block_group, bitmap_blk);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
@ -337,13 +334,8 @@ out:
|
||||
fatal = err;
|
||||
} else {
|
||||
ext4_error(sb, "bit already cleared for inode %lu", ino);
|
||||
if (gdp && !EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
|
||||
int count;
|
||||
count = ext4_free_inodes_count(sb, gdp);
|
||||
percpu_counter_sub(&sbi->s_freeinodes_counter,
|
||||
count);
|
||||
}
|
||||
set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
}
|
||||
|
||||
error_return:
|
||||
@ -914,6 +906,8 @@ repeat_in_this_group:
|
||||
if (group == 0 && (ino + 1) < EXT4_FIRST_INO(sb)) {
|
||||
ext4_error(sb, "reserved inode found cleared - "
|
||||
"inode=%lu", ino + 1);
|
||||
ext4_mark_group_bitmap_corrupted(sb, group,
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
goto next_group;
|
||||
}
|
||||
|
||||
@ -1105,6 +1099,8 @@ got:
|
||||
err = -EIO;
|
||||
ext4_error(sb, "failed to insert inode %lu: doubly allocated?",
|
||||
inode->i_ino);
|
||||
ext4_mark_group_bitmap_corrupted(sb, group,
|
||||
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
|
||||
goto out;
|
||||
}
|
||||
inode->i_generation = prandom_u32();
|
||||
@ -1206,11 +1202,8 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
|
||||
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 (IS_ERR(bitmap_bh)) {
|
||||
ext4_error(sb, "inode bitmap error %ld for orphan %lu",
|
||||
ino, PTR_ERR(bitmap_bh));
|
||||
if (IS_ERR(bitmap_bh))
|
||||
return (struct inode *) bitmap_bh;
|
||||
}
|
||||
|
||||
/* Having the inode bit set should be a 100% indicator that this
|
||||
* is a valid orphan (no e2fsck run on fs). Orphans also include
|
||||
|
@ -561,10 +561,16 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
|
||||
unsigned epb = inode->i_sb->s_blocksize / sizeof(u32);
|
||||
int i;
|
||||
|
||||
/* Count number blocks in a subtree under 'partial' */
|
||||
count = 1;
|
||||
for (i = 0; partial + i != chain + depth - 1; i++)
|
||||
count *= epb;
|
||||
/*
|
||||
* Count number blocks in a subtree under 'partial'. At each
|
||||
* level we count number of complete empty subtrees beyond
|
||||
* current offset and then descend into the subtree only
|
||||
* partially beyond current offset.
|
||||
*/
|
||||
count = 0;
|
||||
for (i = partial - chain + 1; i < depth; i++)
|
||||
count = count * epb + (epb - offsets[i] - 1);
|
||||
count++;
|
||||
/* Fill in size of a hole we found */
|
||||
map->m_pblk = 0;
|
||||
map->m_len = min_t(unsigned int, map->m_len, count);
|
||||
|
@ -144,6 +144,12 @@ int ext4_find_inline_data_nolock(struct inode *inode)
|
||||
goto out;
|
||||
|
||||
if (!is.s.not_found) {
|
||||
if (is.s.here->e_value_inum) {
|
||||
EXT4_ERROR_INODE(inode, "inline data xattr refers "
|
||||
"to an external xattr inode");
|
||||
error = -EFSCORRUPTED;
|
||||
goto out;
|
||||
}
|
||||
EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
|
||||
(void *)ext4_raw_inode(&is.iloc));
|
||||
EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
|
||||
|
@ -4298,28 +4298,28 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
|
||||
EXT4_BLOCK_SIZE_BITS(sb);
|
||||
stop_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);
|
||||
|
||||
/* If there are no blocks to remove, return now */
|
||||
if (first_block >= stop_block)
|
||||
goto out_stop;
|
||||
/* If there are blocks to remove, do it */
|
||||
if (stop_block > first_block) {
|
||||
|
||||
down_write(&EXT4_I(inode)->i_data_sem);
|
||||
ext4_discard_preallocations(inode);
|
||||
down_write(&EXT4_I(inode)->i_data_sem);
|
||||
ext4_discard_preallocations(inode);
|
||||
|
||||
ret = ext4_es_remove_extent(inode, first_block,
|
||||
stop_block - first_block);
|
||||
if (ret) {
|
||||
up_write(&EXT4_I(inode)->i_data_sem);
|
||||
goto out_stop;
|
||||
}
|
||||
|
||||
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
|
||||
ret = ext4_ext_remove_space(inode, first_block,
|
||||
stop_block - 1);
|
||||
else
|
||||
ret = ext4_ind_remove_space(handle, inode, first_block,
|
||||
stop_block);
|
||||
|
||||
ret = ext4_es_remove_extent(inode, first_block,
|
||||
stop_block - first_block);
|
||||
if (ret) {
|
||||
up_write(&EXT4_I(inode)->i_data_sem);
|
||||
goto out_stop;
|
||||
}
|
||||
|
||||
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
|
||||
ret = ext4_ext_remove_space(inode, first_block,
|
||||
stop_block - 1);
|
||||
else
|
||||
ret = ext4_ind_remove_space(handle, inode, first_block,
|
||||
stop_block);
|
||||
|
||||
up_write(&EXT4_I(inode)->i_data_sem);
|
||||
if (IS_SYNC(inode))
|
||||
ext4_handle_sync(handle);
|
||||
|
||||
@ -4701,19 +4701,21 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ext4_iget_extra_inode(struct inode *inode,
|
||||
static inline int ext4_iget_extra_inode(struct inode *inode,
|
||||
struct ext4_inode *raw_inode,
|
||||
struct ext4_inode_info *ei)
|
||||
{
|
||||
__le32 *magic = (void *)raw_inode +
|
||||
EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
|
||||
|
||||
if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize + sizeof(__le32) <=
|
||||
EXT4_INODE_SIZE(inode->i_sb) &&
|
||||
*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
|
||||
ext4_set_inode_state(inode, EXT4_STATE_XATTR);
|
||||
ext4_find_inline_data_nolock(inode);
|
||||
return ext4_find_inline_data_nolock(inode);
|
||||
} else
|
||||
EXT4_I(inode)->i_inline_off = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext4_get_projid(struct inode *inode, kprojid_t *projid)
|
||||
@ -4724,6 +4726,26 @@ int ext4_get_projid(struct inode *inode, kprojid_t *projid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ext4 has self-managed i_version for ea inodes, it stores the lower 32bit of
|
||||
* refcount in i_version, so use raw values if inode has EXT4_EA_INODE_FL flag
|
||||
* set.
|
||||
*/
|
||||
static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val)
|
||||
{
|
||||
if (unlikely(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
|
||||
inode_set_iversion_raw(inode, val);
|
||||
else
|
||||
inode_set_iversion_queried(inode, val);
|
||||
}
|
||||
static inline u64 ext4_inode_peek_iversion(const struct inode *inode)
|
||||
{
|
||||
if (unlikely(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
|
||||
return inode_peek_iversion_raw(inode);
|
||||
else
|
||||
return inode_peek_iversion(inode);
|
||||
}
|
||||
|
||||
struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
||||
{
|
||||
struct ext4_iloc iloc;
|
||||
@ -4893,7 +4915,9 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
||||
ei->i_extra_isize = sizeof(struct ext4_inode) -
|
||||
EXT4_GOOD_OLD_INODE_SIZE;
|
||||
} else {
|
||||
ext4_iget_extra_inode(inode, raw_inode, ei);
|
||||
ret = ext4_iget_extra_inode(inode, raw_inode, ei);
|
||||
if (ret)
|
||||
goto bad_inode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4910,7 +4934,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
||||
ivers |=
|
||||
(__u64)(le32_to_cpu(raw_inode->i_version_hi)) << 32;
|
||||
}
|
||||
inode_set_iversion_queried(inode, ivers);
|
||||
ext4_inode_set_iversion_queried(inode, ivers);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -4945,6 +4969,13 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
||||
inode->i_op = &ext4_dir_inode_operations;
|
||||
inode->i_fop = &ext4_dir_operations;
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
/* VFS does not allow setting these so must be corruption */
|
||||
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) {
|
||||
EXT4_ERROR_INODE(inode,
|
||||
"immutable or append flags not allowed on symlinks");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto bad_inode;
|
||||
}
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
inode->i_op = &ext4_encrypted_symlink_inode_operations;
|
||||
ext4_set_aops(inode);
|
||||
@ -5196,7 +5227,7 @@ static int ext4_do_update_inode(handle_t *handle,
|
||||
}
|
||||
|
||||
if (likely(!test_opt2(inode->i_sb, HURD_COMPAT))) {
|
||||
u64 ivers = inode_peek_iversion(inode);
|
||||
u64 ivers = ext4_inode_peek_iversion(inode);
|
||||
|
||||
raw_inode->i_disk_version = cpu_to_le32(ivers);
|
||||
if (ei->i_extra_isize) {
|
||||
|
@ -470,6 +470,8 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b,
|
||||
"freeing block already freed "
|
||||
"(bit %u)",
|
||||
first + i);
|
||||
ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
}
|
||||
mb_clear_bit(first + i, e4b->bd_info->bb_bitmap);
|
||||
}
|
||||
@ -747,10 +749,8 @@ void ext4_mb_generate_buddy(struct super_block *sb,
|
||||
* corrupt and update bb_free using bitmap value
|
||||
*/
|
||||
grp->bb_free = free;
|
||||
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter,
|
||||
grp->bb_free);
|
||||
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
}
|
||||
mb_set_largest_free_order(sb, grp);
|
||||
|
||||
@ -1454,12 +1454,8 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
|
||||
"freeing already freed block "
|
||||
"(bit %u); block bitmap corrupt.",
|
||||
block);
|
||||
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info))
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter,
|
||||
e4b->bd_info->bb_free);
|
||||
/* Mark the block group as corrupt. */
|
||||
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
|
||||
&e4b->bd_info->bb_state);
|
||||
ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
mb_regenerate_buddy(e4b);
|
||||
goto done;
|
||||
}
|
||||
@ -1956,6 +1952,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
|
||||
"%d free clusters as per "
|
||||
"group info. But bitmap says 0",
|
||||
free);
|
||||
ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1966,6 +1964,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
|
||||
"%d free clusters as per "
|
||||
"group info. But got %d blocks",
|
||||
free, ex.fe_len);
|
||||
ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
|
||||
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
|
||||
/*
|
||||
* The number of free blocks differs. This mostly
|
||||
* indicate that the bitmap is corrupt. So exit
|
||||
@ -2516,8 +2516,7 @@ static void ext4_groupinfo_destroy_slabs(void)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_GRPINFO_CACHES; i++) {
|
||||
if (ext4_groupinfo_caches[i])
|
||||
kmem_cache_destroy(ext4_groupinfo_caches[i]);
|
||||
kmem_cache_destroy(ext4_groupinfo_caches[i]);
|
||||
ext4_groupinfo_caches[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -1933,7 +1933,7 @@ retry:
|
||||
return 0;
|
||||
|
||||
n_group = ext4_get_group_number(sb, n_blocks_count - 1);
|
||||
if (n_group > (0xFFFFFFFFUL / EXT4_INODES_PER_GROUP(sb))) {
|
||||
if (n_group >= (0xFFFFFFFFUL / EXT4_INODES_PER_GROUP(sb))) {
|
||||
ext4_warning(sb, "resize would cause inodes_count overflow");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -763,6 +763,36 @@ __acquires(bitlock)
|
||||
return;
|
||||
}
|
||||
|
||||
void ext4_mark_group_bitmap_corrupted(struct super_block *sb,
|
||||
ext4_group_t group,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
struct ext4_group_info *grp = ext4_get_group_info(sb, group);
|
||||
struct ext4_group_desc *gdp = ext4_get_group_desc(sb, group, NULL);
|
||||
|
||||
if ((flags & EXT4_GROUP_INFO_BBITMAP_CORRUPT) &&
|
||||
!EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) {
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter,
|
||||
grp->bb_free);
|
||||
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
|
||||
&grp->bb_state);
|
||||
}
|
||||
|
||||
if ((flags & EXT4_GROUP_INFO_IBITMAP_CORRUPT) &&
|
||||
!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
|
||||
if (gdp) {
|
||||
int count;
|
||||
|
||||
count = ext4_free_inodes_count(sb, gdp);
|
||||
percpu_counter_sub(&sbi->s_freeinodes_counter,
|
||||
count);
|
||||
}
|
||||
set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT,
|
||||
&grp->bb_state);
|
||||
}
|
||||
}
|
||||
|
||||
void ext4_update_dynamic_rev(struct super_block *sb)
|
||||
{
|
||||
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
|
||||
@ -2116,12 +2146,12 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
|
||||
int read_only)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
int res = 0;
|
||||
int err = 0;
|
||||
|
||||
if (le32_to_cpu(es->s_rev_level) > EXT4_MAX_SUPP_REV) {
|
||||
ext4_msg(sb, KERN_ERR, "revision level too high, "
|
||||
"forcing read-only mode");
|
||||
res = SB_RDONLY;
|
||||
err = -EROFS;
|
||||
}
|
||||
if (read_only)
|
||||
goto done;
|
||||
@ -2154,7 +2184,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
|
||||
if (sbi->s_journal)
|
||||
ext4_set_feature_journal_needs_recovery(sb);
|
||||
|
||||
ext4_commit_super(sb, 1);
|
||||
err = ext4_commit_super(sb, 1);
|
||||
done:
|
||||
if (test_opt(sb, DEBUG))
|
||||
printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
|
||||
@ -2166,7 +2196,7 @@ done:
|
||||
sbi->s_mount_opt, sbi->s_mount_opt2);
|
||||
|
||||
cleancache_init_fs(sb);
|
||||
return res;
|
||||
return err;
|
||||
}
|
||||
|
||||
int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup)
|
||||
@ -4224,8 +4254,12 @@ no_journal:
|
||||
goto failed_mount4;
|
||||
}
|
||||
|
||||
if (ext4_setup_super(sb, es, sb_rdonly(sb)))
|
||||
ret = ext4_setup_super(sb, es, sb_rdonly(sb));
|
||||
if (ret == -EROFS) {
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
ret = 0;
|
||||
} else if (ret)
|
||||
goto failed_mount4a;
|
||||
|
||||
/* determine the minimum size of new large inodes, if present */
|
||||
if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE &&
|
||||
@ -4760,11 +4794,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
||||
unlock_buffer(sbh);
|
||||
error = __sync_dirty_buffer(sbh,
|
||||
REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = buffer_write_io_error(sbh);
|
||||
if (error) {
|
||||
if (buffer_write_io_error(sbh)) {
|
||||
ext4_msg(sb, KERN_ERR, "I/O error while writing "
|
||||
"superblock");
|
||||
clear_buffer_write_io_error(sbh);
|
||||
@ -5165,8 +5195,12 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
|
||||
if (sbi->s_journal)
|
||||
ext4_clear_journal_err(sb, es);
|
||||
sbi->s_mount_state = le16_to_cpu(es->s_state);
|
||||
if (!ext4_setup_super(sb, es, 0))
|
||||
sb->s_flags &= ~SB_RDONLY;
|
||||
|
||||
err = ext4_setup_super(sb, es, 0);
|
||||
if (err)
|
||||
goto restore_opts;
|
||||
|
||||
sb->s_flags &= ~SB_RDONLY;
|
||||
if (ext4_has_feature_mmp(sb))
|
||||
if (ext4_multi_mount_protect(sb,
|
||||
le64_to_cpu(es->s_mmp_block))) {
|
||||
@ -5190,8 +5224,11 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
|
||||
}
|
||||
|
||||
ext4_setup_system_zone(sb);
|
||||
if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY))
|
||||
ext4_commit_super(sb, 1);
|
||||
if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY)) {
|
||||
err = ext4_commit_super(sb, 1);
|
||||
if (err)
|
||||
goto restore_opts;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Release old quota file names */
|
||||
@ -5252,7 +5289,8 @@ static int ext4_statfs_project(struct super_block *sb,
|
||||
dquot->dq_dqb.dqb_bsoftlimit :
|
||||
dquot->dq_dqb.dqb_bhardlimit) >> sb->s_blocksize_bits;
|
||||
if (limit && buf->f_blocks > limit) {
|
||||
curblock = dquot->dq_dqb.dqb_curspace >> sb->s_blocksize_bits;
|
||||
curblock = (dquot->dq_dqb.dqb_curspace +
|
||||
dquot->dq_dqb.dqb_rsvspace) >> sb->s_blocksize_bits;
|
||||
buf->f_blocks = limit;
|
||||
buf->f_bfree = buf->f_bavail =
|
||||
(buf->f_blocks > curblock) ?
|
||||
|
@ -1688,7 +1688,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
|
||||
|
||||
/* No failures allowed past this point. */
|
||||
|
||||
if (!s->not_found && here->e_value_offs) {
|
||||
if (!s->not_found && here->e_value_size && here->e_value_offs) {
|
||||
/* Remove the old value. */
|
||||
void *first_val = s->base + min_offs;
|
||||
size_t offs = le16_to_cpu(here->e_value_offs);
|
||||
|
@ -43,7 +43,7 @@ ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
|
||||
err = ext4_xattr_set_handle(handle, inode,
|
||||
EXT4_XATTR_INDEX_SECURITY,
|
||||
xattr->name, xattr->value,
|
||||
xattr->value_len, 0);
|
||||
xattr->value_len, XATTR_CREATE);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ void __jbd2_debug(int level, const char *file, const char *func,
|
||||
va_start(args, fmt);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
printk(KERN_DEBUG "%s: (%s, %u): %pV\n", file, func, line, &vaf);
|
||||
printk(KERN_DEBUG "%s: (%s, %u): %pV", file, func, line, &vaf);
|
||||
va_end(args);
|
||||
}
|
||||
EXPORT_SYMBOL(__jbd2_debug);
|
||||
@ -2302,8 +2302,7 @@ static void jbd2_journal_destroy_slabs(void)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < JBD2_MAX_SLABS; i++) {
|
||||
if (jbd2_slab[i])
|
||||
kmem_cache_destroy(jbd2_slab[i]);
|
||||
kmem_cache_destroy(jbd2_slab[i]);
|
||||
jbd2_slab[i] = NULL;
|
||||
}
|
||||
}
|
||||
@ -2404,10 +2403,8 @@ static int jbd2_journal_init_journal_head_cache(void)
|
||||
|
||||
static void jbd2_journal_destroy_journal_head_cache(void)
|
||||
{
|
||||
if (jbd2_journal_head_cache) {
|
||||
kmem_cache_destroy(jbd2_journal_head_cache);
|
||||
jbd2_journal_head_cache = NULL;
|
||||
}
|
||||
kmem_cache_destroy(jbd2_journal_head_cache);
|
||||
jbd2_journal_head_cache = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2665,11 +2662,10 @@ static int __init jbd2_journal_init_handle_cache(void)
|
||||
|
||||
static void jbd2_journal_destroy_handle_cache(void)
|
||||
{
|
||||
if (jbd2_handle_cache)
|
||||
kmem_cache_destroy(jbd2_handle_cache);
|
||||
if (jbd2_inode_cache)
|
||||
kmem_cache_destroy(jbd2_inode_cache);
|
||||
|
||||
kmem_cache_destroy(jbd2_handle_cache);
|
||||
jbd2_handle_cache = NULL;
|
||||
kmem_cache_destroy(jbd2_inode_cache);
|
||||
jbd2_inode_cache = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -180,14 +180,10 @@ static struct jbd2_revoke_record_s *find_revoke_record(journal_t *journal,
|
||||
|
||||
void jbd2_journal_destroy_revoke_caches(void)
|
||||
{
|
||||
if (jbd2_revoke_record_cache) {
|
||||
kmem_cache_destroy(jbd2_revoke_record_cache);
|
||||
jbd2_revoke_record_cache = NULL;
|
||||
}
|
||||
if (jbd2_revoke_table_cache) {
|
||||
kmem_cache_destroy(jbd2_revoke_table_cache);
|
||||
jbd2_revoke_table_cache = NULL;
|
||||
}
|
||||
kmem_cache_destroy(jbd2_revoke_record_cache);
|
||||
jbd2_revoke_record_cache = NULL;
|
||||
kmem_cache_destroy(jbd2_revoke_table_cache);
|
||||
jbd2_revoke_table_cache = NULL;
|
||||
}
|
||||
|
||||
int __init jbd2_journal_init_revoke_caches(void)
|
||||
|
@ -49,10 +49,8 @@ int __init jbd2_journal_init_transaction_cache(void)
|
||||
|
||||
void jbd2_journal_destroy_transaction_cache(void)
|
||||
{
|
||||
if (transaction_cache) {
|
||||
kmem_cache_destroy(transaction_cache);
|
||||
transaction_cache = NULL;
|
||||
}
|
||||
kmem_cache_destroy(transaction_cache);
|
||||
transaction_cache = NULL;
|
||||
}
|
||||
|
||||
void jbd2_journal_free_transaction(transaction_t *transaction)
|
||||
|
@ -1597,6 +1597,11 @@ static inline void sb_start_intwrite(struct super_block *sb)
|
||||
__sb_start_write(sb, SB_FREEZE_FS, true);
|
||||
}
|
||||
|
||||
static inline int sb_start_intwrite_trylock(struct super_block *sb)
|
||||
{
|
||||
return __sb_start_write(sb, SB_FREEZE_FS, false);
|
||||
}
|
||||
|
||||
|
||||
extern bool inode_owner_or_capable(const struct inode *inode);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user