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:
Linus Torvalds 2018-06-05 12:49:17 -07:00
commit 1434763ca5
18 changed files with 245 additions and 167 deletions

View File

@ -185,25 +185,15 @@ static int ext4_init_block_bitmap(struct super_block *sb,
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_fsblk_t start, tmp; ext4_fsblk_t start, tmp;
int flex_bg = 0; int flex_bg = 0;
struct ext4_group_info *grp;
J_ASSERT_BH(bh, buffer_locked(bh)); J_ASSERT_BH(bh, buffer_locked(bh));
/* If checksum is bad mark all blocks used to prevent allocation /* If checksum is bad mark all blocks used to prevent allocation
* essentially implementing a per-group read-only flag. */ * essentially implementing a per-group read-only flag. */
if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) { if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
grp = ext4_get_group_info(sb, block_group); ext4_mark_group_bitmap_corrupted(sb, block_group,
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) EXT4_GROUP_INFO_BBITMAP_CORRUPT |
percpu_counter_sub(&sbi->s_freeclusters_counter, EXT4_GROUP_INFO_IBITMAP_CORRUPT);
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);
return -EFSBADCRC; return -EFSBADCRC;
} }
memset(bh->b_data, 0, sb->s_blocksize); 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; ext4_fsblk_t blk;
struct ext4_group_info *grp = ext4_get_group_info(sb, block_group); struct ext4_group_info *grp = ext4_get_group_info(sb, block_group);
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (buffer_verified(bh)) if (buffer_verified(bh))
return 0; return 0;
@ -387,10 +376,8 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
desc, bh))) { desc, bh))) {
ext4_unlock_group(sb, block_group); ext4_unlock_group(sb, block_group);
ext4_error(sb, "bg %u: bad block bitmap checksum", block_group); ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) ext4_mark_group_bitmap_corrupted(sb, block_group,
percpu_counter_sub(&sbi->s_freeclusters_counter, EXT4_GROUP_INFO_BBITMAP_CORRUPT);
grp->bb_free);
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
return -EFSBADCRC; return -EFSBADCRC;
} }
blk = ext4_valid_block_bitmap(sb, desc, block_group, bh); 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_unlock_group(sb, block_group);
ext4_error(sb, "bg %u: block %llu: invalid block bitmap", ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
block_group, blk); block_group, blk);
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) ext4_mark_group_bitmap_corrupted(sb, block_group,
percpu_counter_sub(&sbi->s_freeclusters_counter, EXT4_GROUP_INFO_BBITMAP_CORRUPT);
grp->bb_free);
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
set_buffer_verified(bh); 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))) { (bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
ext4_error(sb, "Invalid block bitmap block %llu in " ext4_error(sb, "Invalid block bitmap block %llu in "
"block_group %u", bitmap_blk, block_group); "block_group %u", bitmap_blk, block_group);
ext4_mark_group_bitmap_corrupted(sb, block_group,
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
return ERR_PTR(-EFSCORRUPTED); return ERR_PTR(-EFSCORRUPTED);
} }
bh = sb_getblk(sb, bitmap_blk); 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 - " ext4_error(sb, "Cannot read block bitmap - "
"block_group = %u, block_bitmap = %llu", "block_group = %u, block_bitmap = %llu",
block_group, (unsigned long long) bh->b_blocknr); block_group, (unsigned long long) bh->b_blocknr);
ext4_mark_group_bitmap_corrupted(sb, block_group,
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
return -EIO; return -EIO;
} }
clear_buffer_new(bh); clear_buffer_new(bh);

View File

@ -2530,6 +2530,9 @@ extern int ext4_alloc_flex_bg_array(struct super_block *sb,
ext4_group_t ngroup); ext4_group_t ngroup);
extern const char *ext4_decode_error(struct super_block *sb, int errno, extern const char *ext4_decode_error(struct super_block *sb, int errno,
char nbuf[16]); 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) extern __printf(4, 5)
void __ext4_error(struct super_block *, const char *, unsigned int, 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_WAS_TRIMMED_BIT 1
#define EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT 2 #define EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT 2
#define EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT 3 #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) \ #define EXT4_MB_GRP_NEED_INIT(grp) \
(test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state))) (test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state)))

View File

@ -162,8 +162,7 @@ int __init ext4_init_es(void)
void ext4_exit_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) void ext4_es_init_tree(struct ext4_es_tree *tree)

View File

@ -277,10 +277,11 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
} }
#ifdef CONFIG_FS_DAX #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) enum page_entry_size pe_size)
{ {
int result, error = 0; int error = 0;
vm_fault_t result;
int retries = 0; int retries = 0;
handle_t *handle = NULL; handle_t *handle = NULL;
struct inode *inode = file_inode(vmf->vma->vm_file); struct inode *inode = file_inode(vmf->vma->vm_file);
@ -335,7 +336,7 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
return result; 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); 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; 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(sb);
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
struct vfsmount *mnt = filp->f_path.mnt;
struct path path; struct path path;
char buf[64], *cp; 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; int ret;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO; return -EIO;
if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) && ret = ext4_sample_last_mounted(inode->i_sb, filp->f_path.mnt);
!sb_rdonly(sb))) { if (ret)
sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED; return ret;
/*
* 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 = fscrypt_file_open(inode, filp); ret = fscrypt_file_open(inode, filp);
if (ret) if (ret)

View File

@ -402,8 +402,8 @@ static void ext4_getfsmap_free_fixed_metadata(struct list_head *meta_list)
} }
/* Find all the fixed metadata in the filesystem. */ /* Find all the fixed metadata in the filesystem. */
int ext4_getfsmap_find_fixed_metadata(struct super_block *sb, static int ext4_getfsmap_find_fixed_metadata(struct super_block *sb,
struct list_head *meta_list) struct list_head *meta_list)
{ {
struct ext4_group_desc *gdp; struct ext4_group_desc *gdp;
ext4_group_t agno; ext4_group_t agno;

View File

@ -83,7 +83,6 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
{ {
ext4_fsblk_t blk; ext4_fsblk_t blk;
struct ext4_group_info *grp = ext4_get_group_info(sb, block_group); struct ext4_group_info *grp = ext4_get_group_info(sb, block_group);
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (buffer_verified(bh)) if (buffer_verified(bh))
return 0; return 0;
@ -97,14 +96,8 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
ext4_unlock_group(sb, block_group); ext4_unlock_group(sb, block_group);
ext4_error(sb, "Corrupt inode bitmap - block_group = %u, " ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
"inode_bitmap = %llu", block_group, blk); "inode_bitmap = %llu", block_group, blk);
grp = ext4_get_group_info(sb, block_group); ext4_mark_group_bitmap_corrupted(sb, block_group,
if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) { EXT4_GROUP_INFO_IBITMAP_CORRUPT);
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);
return -EFSBADCRC; return -EFSBADCRC;
} }
set_buffer_verified(bh); 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))) { (bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
ext4_error(sb, "Invalid inode bitmap blk %llu in " ext4_error(sb, "Invalid inode bitmap blk %llu in "
"block_group %u", bitmap_blk, block_group); "block_group %u", bitmap_blk, block_group);
ext4_mark_group_bitmap_corrupted(sb, block_group,
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
return ERR_PTR(-EFSCORRUPTED); return ERR_PTR(-EFSCORRUPTED);
} }
bh = sb_getblk(sb, bitmap_blk); 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 - " ext4_error(sb, "Cannot read inode bitmap - "
"block_group = %u, inode_bitmap = %llu", "block_group = %u, inode_bitmap = %llu",
block_group, bitmap_blk); block_group, bitmap_blk);
return ERR_PTR(-EIO); return ERR_PTR(-ENOMEM);
} }
if (bitmap_uptodate(bh)) if (bitmap_uptodate(bh))
goto verify; 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 - " ext4_error(sb, "Cannot read inode bitmap - "
"block_group = %u, inode_bitmap = %llu", "block_group = %u, inode_bitmap = %llu",
block_group, bitmap_blk); block_group, bitmap_blk);
ext4_mark_group_bitmap_corrupted(sb, block_group,
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
@ -337,13 +334,8 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
fatal = err; fatal = err;
} else { } else {
ext4_error(sb, "bit already cleared for inode %lu", ino); ext4_error(sb, "bit already cleared for inode %lu", ino);
if (gdp && !EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) { ext4_mark_group_bitmap_corrupted(sb, block_group,
int count; EXT4_GROUP_INFO_IBITMAP_CORRUPT);
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);
} }
error_return: error_return:
@ -914,6 +906,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
if (group == 0 && (ino + 1) < EXT4_FIRST_INO(sb)) { if (group == 0 && (ino + 1) < EXT4_FIRST_INO(sb)) {
ext4_error(sb, "reserved inode found cleared - " ext4_error(sb, "reserved inode found cleared - "
"inode=%lu", ino + 1); "inode=%lu", ino + 1);
ext4_mark_group_bitmap_corrupted(sb, group,
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
goto next_group; goto next_group;
} }
@ -1105,6 +1099,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
err = -EIO; err = -EIO;
ext4_error(sb, "failed to insert inode %lu: doubly allocated?", ext4_error(sb, "failed to insert inode %lu: doubly allocated?",
inode->i_ino); inode->i_ino);
ext4_mark_group_bitmap_corrupted(sb, group,
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
goto out; goto out;
} }
inode->i_generation = prandom_u32(); 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); block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb); bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb);
bitmap_bh = ext4_read_inode_bitmap(sb, block_group); bitmap_bh = ext4_read_inode_bitmap(sb, block_group);
if (IS_ERR(bitmap_bh)) { if (IS_ERR(bitmap_bh))
ext4_error(sb, "inode bitmap error %ld for orphan %lu",
ino, PTR_ERR(bitmap_bh));
return (struct inode *) bitmap_bh; return (struct inode *) bitmap_bh;
}
/* Having the inode bit set should be a 100% indicator that this /* Having the inode bit set should be a 100% indicator that this
* is a valid orphan (no e2fsck run on fs). Orphans also include * is a valid orphan (no e2fsck run on fs). Orphans also include

View File

@ -561,10 +561,16 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
unsigned epb = inode->i_sb->s_blocksize / sizeof(u32); unsigned epb = inode->i_sb->s_blocksize / sizeof(u32);
int i; int i;
/* Count number blocks in a subtree under 'partial' */ /*
count = 1; * Count number blocks in a subtree under 'partial'. At each
for (i = 0; partial + i != chain + depth - 1; i++) * level we count number of complete empty subtrees beyond
count *= epb; * 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 */ /* Fill in size of a hole we found */
map->m_pblk = 0; map->m_pblk = 0;
map->m_len = min_t(unsigned int, map->m_len, count); map->m_len = min_t(unsigned int, map->m_len, count);

View File

@ -144,6 +144,12 @@ int ext4_find_inline_data_nolock(struct inode *inode)
goto out; goto out;
if (!is.s.not_found) { 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 - EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
(void *)ext4_raw_inode(&is.iloc)); (void *)ext4_raw_inode(&is.iloc));
EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE + EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +

View File

@ -4298,28 +4298,28 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
EXT4_BLOCK_SIZE_BITS(sb); EXT4_BLOCK_SIZE_BITS(sb);
stop_block = (offset + 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 there are blocks to remove, do it */
if (first_block >= stop_block) if (stop_block > first_block) {
goto out_stop;
down_write(&EXT4_I(inode)->i_data_sem); down_write(&EXT4_I(inode)->i_data_sem);
ext4_discard_preallocations(inode); 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); 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)) if (IS_SYNC(inode))
ext4_handle_sync(handle); 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 *raw_inode,
struct ext4_inode_info *ei) struct ext4_inode_info *ei)
{ {
__le32 *magic = (void *)raw_inode + __le32 *magic = (void *)raw_inode +
EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize; EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize + sizeof(__le32) <= if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize + sizeof(__le32) <=
EXT4_INODE_SIZE(inode->i_sb) && EXT4_INODE_SIZE(inode->i_sb) &&
*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) { *magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
ext4_set_inode_state(inode, EXT4_STATE_XATTR); ext4_set_inode_state(inode, EXT4_STATE_XATTR);
ext4_find_inline_data_nolock(inode); return ext4_find_inline_data_nolock(inode);
} else } else
EXT4_I(inode)->i_inline_off = 0; EXT4_I(inode)->i_inline_off = 0;
return 0;
} }
int ext4_get_projid(struct inode *inode, kprojid_t *projid) 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; 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 inode *ext4_iget(struct super_block *sb, unsigned long ino)
{ {
struct ext4_iloc iloc; 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) - ei->i_extra_isize = sizeof(struct ext4_inode) -
EXT4_GOOD_OLD_INODE_SIZE; EXT4_GOOD_OLD_INODE_SIZE;
} else { } 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 |= ivers |=
(__u64)(le32_to_cpu(raw_inode->i_version_hi)) << 32; (__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; 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_op = &ext4_dir_inode_operations;
inode->i_fop = &ext4_dir_operations; inode->i_fop = &ext4_dir_operations;
} else if (S_ISLNK(inode->i_mode)) { } 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)) { if (ext4_encrypted_inode(inode)) {
inode->i_op = &ext4_encrypted_symlink_inode_operations; inode->i_op = &ext4_encrypted_symlink_inode_operations;
ext4_set_aops(inode); 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))) { 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); raw_inode->i_disk_version = cpu_to_le32(ivers);
if (ei->i_extra_isize) { if (ei->i_extra_isize) {

View File

@ -470,6 +470,8 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b,
"freeing block already freed " "freeing block already freed "
"(bit %u)", "(bit %u)",
first + i); 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); 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 * corrupt and update bb_free using bitmap value
*/ */
grp->bb_free = free; grp->bb_free = free;
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) ext4_mark_group_bitmap_corrupted(sb, group,
percpu_counter_sub(&sbi->s_freeclusters_counter, EXT4_GROUP_INFO_BBITMAP_CORRUPT);
grp->bb_free);
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
} }
mb_set_largest_free_order(sb, grp); 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 " "freeing already freed block "
"(bit %u); block bitmap corrupt.", "(bit %u); block bitmap corrupt.",
block); block);
if (!EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info)) ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
percpu_counter_sub(&sbi->s_freeclusters_counter, EXT4_GROUP_INFO_BBITMAP_CORRUPT);
e4b->bd_info->bb_free);
/* Mark the block group as corrupt. */
set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT,
&e4b->bd_info->bb_state);
mb_regenerate_buddy(e4b); mb_regenerate_buddy(e4b);
goto done; goto done;
} }
@ -1956,6 +1952,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
"%d free clusters as per " "%d free clusters as per "
"group info. But bitmap says 0", "group info. But bitmap says 0",
free); free);
ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
break; break;
} }
@ -1966,6 +1964,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
"%d free clusters as per " "%d free clusters as per "
"group info. But got %d blocks", "group info. But got %d blocks",
free, ex.fe_len); 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 * The number of free blocks differs. This mostly
* indicate that the bitmap is corrupt. So exit * indicate that the bitmap is corrupt. So exit
@ -2516,8 +2516,7 @@ static void ext4_groupinfo_destroy_slabs(void)
int i; int i;
for (i = 0; i < NR_GRPINFO_CACHES; 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; ext4_groupinfo_caches[i] = NULL;
} }
} }

View File

@ -1933,7 +1933,7 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
return 0; return 0;
n_group = ext4_get_group_number(sb, n_blocks_count - 1); 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"); ext4_warning(sb, "resize would cause inodes_count overflow");
return -EINVAL; return -EINVAL;
} }

View File

@ -763,6 +763,36 @@ __acquires(bitlock)
return; 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) void ext4_update_dynamic_rev(struct super_block *sb)
{ {
struct ext4_super_block *es = EXT4_SB(sb)->s_es; 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) int read_only)
{ {
struct ext4_sb_info *sbi = EXT4_SB(sb); 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) { if (le32_to_cpu(es->s_rev_level) > EXT4_MAX_SUPP_REV) {
ext4_msg(sb, KERN_ERR, "revision level too high, " ext4_msg(sb, KERN_ERR, "revision level too high, "
"forcing read-only mode"); "forcing read-only mode");
res = SB_RDONLY; err = -EROFS;
} }
if (read_only) if (read_only)
goto done; goto done;
@ -2154,7 +2184,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
if (sbi->s_journal) if (sbi->s_journal)
ext4_set_feature_journal_needs_recovery(sb); ext4_set_feature_journal_needs_recovery(sb);
ext4_commit_super(sb, 1); err = ext4_commit_super(sb, 1);
done: done:
if (test_opt(sb, DEBUG)) if (test_opt(sb, DEBUG))
printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, " printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
@ -2166,7 +2196,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
sbi->s_mount_opt, sbi->s_mount_opt2); sbi->s_mount_opt, sbi->s_mount_opt2);
cleancache_init_fs(sb); cleancache_init_fs(sb);
return res; return err;
} }
int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup) int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup)
@ -4224,8 +4254,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
goto failed_mount4; 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; sb->s_flags |= SB_RDONLY;
ret = 0;
} else if (ret)
goto failed_mount4a;
/* determine the minimum size of new large inodes, if present */ /* determine the minimum size of new large inodes, if present */
if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE && 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); unlock_buffer(sbh);
error = __sync_dirty_buffer(sbh, error = __sync_dirty_buffer(sbh,
REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0)); REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
if (error) if (buffer_write_io_error(sbh)) {
return error;
error = buffer_write_io_error(sbh);
if (error) {
ext4_msg(sb, KERN_ERR, "I/O error while writing " ext4_msg(sb, KERN_ERR, "I/O error while writing "
"superblock"); "superblock");
clear_buffer_write_io_error(sbh); 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) if (sbi->s_journal)
ext4_clear_journal_err(sb, es); ext4_clear_journal_err(sb, es);
sbi->s_mount_state = le16_to_cpu(es->s_state); 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_has_feature_mmp(sb))
if (ext4_multi_mount_protect(sb, if (ext4_multi_mount_protect(sb,
le64_to_cpu(es->s_mmp_block))) { 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); ext4_setup_system_zone(sb);
if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY)) if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY)) {
ext4_commit_super(sb, 1); err = ext4_commit_super(sb, 1);
if (err)
goto restore_opts;
}
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Release old quota file names */ /* 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_bsoftlimit :
dquot->dq_dqb.dqb_bhardlimit) >> sb->s_blocksize_bits; dquot->dq_dqb.dqb_bhardlimit) >> sb->s_blocksize_bits;
if (limit && buf->f_blocks > limit) { 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_blocks = limit;
buf->f_bfree = buf->f_bavail = buf->f_bfree = buf->f_bavail =
(buf->f_blocks > curblock) ? (buf->f_blocks > curblock) ?

View File

@ -1688,7 +1688,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
/* No failures allowed past this point. */ /* 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. */ /* Remove the old value. */
void *first_val = s->base + min_offs; void *first_val = s->base + min_offs;
size_t offs = le16_to_cpu(here->e_value_offs); size_t offs = le16_to_cpu(here->e_value_offs);

View File

@ -43,7 +43,7 @@ ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
err = ext4_xattr_set_handle(handle, inode, err = ext4_xattr_set_handle(handle, inode,
EXT4_XATTR_INDEX_SECURITY, EXT4_XATTR_INDEX_SECURITY,
xattr->name, xattr->value, xattr->name, xattr->value,
xattr->value_len, 0); xattr->value_len, XATTR_CREATE);
if (err < 0) if (err < 0)
break; break;
} }

View File

@ -114,7 +114,7 @@ void __jbd2_debug(int level, const char *file, const char *func,
va_start(args, fmt); va_start(args, fmt);
vaf.fmt = fmt; vaf.fmt = fmt;
vaf.va = &args; 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); va_end(args);
} }
EXPORT_SYMBOL(__jbd2_debug); EXPORT_SYMBOL(__jbd2_debug);
@ -2302,8 +2302,7 @@ static void jbd2_journal_destroy_slabs(void)
int i; int i;
for (i = 0; i < JBD2_MAX_SLABS; 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; 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) static void jbd2_journal_destroy_journal_head_cache(void)
{ {
if (jbd2_journal_head_cache) { kmem_cache_destroy(jbd2_journal_head_cache);
kmem_cache_destroy(jbd2_journal_head_cache); jbd2_journal_head_cache = NULL;
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) static void jbd2_journal_destroy_handle_cache(void)
{ {
if (jbd2_handle_cache) kmem_cache_destroy(jbd2_handle_cache);
kmem_cache_destroy(jbd2_handle_cache); jbd2_handle_cache = NULL;
if (jbd2_inode_cache) kmem_cache_destroy(jbd2_inode_cache);
kmem_cache_destroy(jbd2_inode_cache); jbd2_inode_cache = NULL;
} }
/* /*

View File

@ -180,14 +180,10 @@ static struct jbd2_revoke_record_s *find_revoke_record(journal_t *journal,
void jbd2_journal_destroy_revoke_caches(void) void jbd2_journal_destroy_revoke_caches(void)
{ {
if (jbd2_revoke_record_cache) { kmem_cache_destroy(jbd2_revoke_record_cache);
kmem_cache_destroy(jbd2_revoke_record_cache); jbd2_revoke_record_cache = NULL;
jbd2_revoke_record_cache = NULL; kmem_cache_destroy(jbd2_revoke_table_cache);
} jbd2_revoke_table_cache = NULL;
if (jbd2_revoke_table_cache) {
kmem_cache_destroy(jbd2_revoke_table_cache);
jbd2_revoke_table_cache = NULL;
}
} }
int __init jbd2_journal_init_revoke_caches(void) int __init jbd2_journal_init_revoke_caches(void)

View File

@ -49,10 +49,8 @@ int __init jbd2_journal_init_transaction_cache(void)
void jbd2_journal_destroy_transaction_cache(void) void jbd2_journal_destroy_transaction_cache(void)
{ {
if (transaction_cache) { kmem_cache_destroy(transaction_cache);
kmem_cache_destroy(transaction_cache); transaction_cache = NULL;
transaction_cache = NULL;
}
} }
void jbd2_journal_free_transaction(transaction_t *transaction) void jbd2_journal_free_transaction(transaction_t *transaction)

View File

@ -1597,6 +1597,11 @@ static inline void sb_start_intwrite(struct super_block *sb)
__sb_start_write(sb, SB_FREEZE_FS, true); __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); extern bool inode_owner_or_capable(const struct inode *inode);