Commit ad3273d5 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Ted writes:
	Various ext4 bug fixes; primarily making ext4 more robust against
	maliciously crafted file systems, and some DAX fixes.

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4, dax: set ext4_dax_aops for dax files
  ext4, dax: add ext4_bmap to ext4_dax_aops
  ext4: don't mark mmp buffer head dirty
  ext4: show test_dummy_encryption mount option in /proc/mounts
  ext4: close race between direct IO and ext4_break_layouts()
  ext4: fix online resizing for bigalloc file systems with a 1k block size
  ext4: fix online resize's handling of a too-small final block group
  ext4: recalucate superblock checksum after updating free blocks/inodes
  ext4: avoid arithemetic overflow that can trigger a BUG
  ext4: avoid divide by zero fault when deleting corrupted inline directories
  ext4: check to make sure the rename(2)'s destination is not freed
  ext4: add nonstring annotations to ext4.h
parents c0747ad3 cce6c9f7
...@@ -76,7 +76,7 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, ...@@ -76,7 +76,7 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len))) else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
error_msg = "rec_len is too small for name_len"; error_msg = "rec_len is too small for name_len";
else if (unlikely(((char *) de - buf) + rlen > size)) else if (unlikely(((char *) de - buf) + rlen > size))
error_msg = "directory entry across range"; error_msg = "directory entry overrun";
else if (unlikely(le32_to_cpu(de->inode) > else if (unlikely(le32_to_cpu(de->inode) >
le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count))) le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
error_msg = "inode out of bounds"; error_msg = "inode out of bounds";
...@@ -85,18 +85,16 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, ...@@ -85,18 +85,16 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
if (filp) if (filp)
ext4_error_file(filp, function, line, bh->b_blocknr, ext4_error_file(filp, function, line, bh->b_blocknr,
"bad entry in directory: %s - offset=%u(%u), " "bad entry in directory: %s - offset=%u, "
"inode=%u, rec_len=%d, name_len=%d", "inode=%u, rec_len=%d, name_len=%d, size=%d",
error_msg, (unsigned) (offset % size), error_msg, offset, le32_to_cpu(de->inode),
offset, le32_to_cpu(de->inode), rlen, de->name_len, size);
rlen, de->name_len);
else else
ext4_error_inode(dir, function, line, bh->b_blocknr, ext4_error_inode(dir, function, line, bh->b_blocknr,
"bad entry in directory: %s - offset=%u(%u), " "bad entry in directory: %s - offset=%u, "
"inode=%u, rec_len=%d, name_len=%d", "inode=%u, rec_len=%d, name_len=%d, size=%d",
error_msg, (unsigned) (offset % size), error_msg, offset, le32_to_cpu(de->inode),
offset, le32_to_cpu(de->inode), rlen, de->name_len, size);
rlen, de->name_len);
return 1; return 1;
} }
......
...@@ -43,6 +43,17 @@ ...@@ -43,6 +43,17 @@
#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) #define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION)
#include <linux/fscrypt.h> #include <linux/fscrypt.h>
#include <linux/compiler.h>
/* Until this gets included into linux/compiler-gcc.h */
#ifndef __nonstring
#if defined(GCC_VERSION) && (GCC_VERSION >= 80000)
#define __nonstring __attribute__((nonstring))
#else
#define __nonstring
#endif
#endif
/* /*
* The fourth extended filesystem constants/structures * The fourth extended filesystem constants/structures
*/ */
...@@ -675,6 +686,9 @@ enum { ...@@ -675,6 +686,9 @@ enum {
/* Max physical block we can address w/o extents */ /* Max physical block we can address w/o extents */
#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF #define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF
/* Max logical block we can support */
#define EXT4_MAX_LOGICAL_BLOCK 0xFFFFFFFF
/* /*
* Structure of an inode on the disk * Structure of an inode on the disk
*/ */
...@@ -1226,7 +1240,7 @@ struct ext4_super_block { ...@@ -1226,7 +1240,7 @@ struct ext4_super_block {
__le32 s_feature_ro_compat; /* readonly-compatible feature set */ __le32 s_feature_ro_compat; /* readonly-compatible feature set */
/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */ /*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */
/*78*/ char s_volume_name[16]; /* volume name */ /*78*/ char s_volume_name[16]; /* volume name */
/*88*/ char s_last_mounted[64]; /* directory where last mounted */ /*88*/ char s_last_mounted[64] __nonstring; /* directory where last mounted */
/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */ /*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */
/* /*
* Performance hints. Directory preallocation should only * Performance hints. Directory preallocation should only
...@@ -1277,13 +1291,13 @@ struct ext4_super_block { ...@@ -1277,13 +1291,13 @@ struct ext4_super_block {
__le32 s_first_error_time; /* first time an error happened */ __le32 s_first_error_time; /* first time an error happened */
__le32 s_first_error_ino; /* inode involved in first error */ __le32 s_first_error_ino; /* inode involved in first error */
__le64 s_first_error_block; /* block involved of first error */ __le64 s_first_error_block; /* block involved of first error */
__u8 s_first_error_func[32]; /* function where the error happened */ __u8 s_first_error_func[32] __nonstring; /* function where the error happened */
__le32 s_first_error_line; /* line number where error happened */ __le32 s_first_error_line; /* line number where error happened */
__le32 s_last_error_time; /* most recent time of an error */ __le32 s_last_error_time; /* most recent time of an error */
__le32 s_last_error_ino; /* inode involved in last error */ __le32 s_last_error_ino; /* inode involved in last error */
__le32 s_last_error_line; /* line number where error happened */ __le32 s_last_error_line; /* line number where error happened */
__le64 s_last_error_block; /* block involved of last error */ __le64 s_last_error_block; /* block involved of last error */
__u8 s_last_error_func[32]; /* function where the error happened */ __u8 s_last_error_func[32] __nonstring; /* function where the error happened */
#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts) #define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts)
__u8 s_mount_opts[64]; __u8 s_mount_opts[64];
__le32 s_usr_quota_inum; /* inode for tracking user quota */ __le32 s_usr_quota_inum; /* inode for tracking user quota */
......
...@@ -1753,6 +1753,7 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data) ...@@ -1753,6 +1753,7 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
{ {
int err, inline_size; int err, inline_size;
struct ext4_iloc iloc; struct ext4_iloc iloc;
size_t inline_len;
void *inline_pos; void *inline_pos;
unsigned int offset; unsigned int offset;
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
...@@ -1780,8 +1781,9 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data) ...@@ -1780,8 +1781,9 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
goto out; goto out;
} }
inline_len = ext4_get_inline_size(dir);
offset = EXT4_INLINE_DOTDOT_SIZE; offset = EXT4_INLINE_DOTDOT_SIZE;
while (offset < dir->i_size) { while (offset < inline_len) {
de = ext4_get_inline_entry(dir, &iloc, offset, de = ext4_get_inline_entry(dir, &iloc, offset,
&inline_pos, &inline_size); &inline_pos, &inline_size);
if (ext4_check_dir_entry(dir, NULL, de, if (ext4_check_dir_entry(dir, NULL, de,
......
...@@ -3413,12 +3413,16 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length, ...@@ -3413,12 +3413,16 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
{ {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
unsigned int blkbits = inode->i_blkbits; unsigned int blkbits = inode->i_blkbits;
unsigned long first_block = offset >> blkbits; unsigned long first_block, last_block;
unsigned long last_block = (offset + length - 1) >> blkbits;
struct ext4_map_blocks map; struct ext4_map_blocks map;
bool delalloc = false; bool delalloc = false;
int ret; int ret;
if ((offset >> blkbits) > EXT4_MAX_LOGICAL_BLOCK)
return -EINVAL;
first_block = offset >> blkbits;
last_block = min_t(loff_t, (offset + length - 1) >> blkbits,
EXT4_MAX_LOGICAL_BLOCK);
if (flags & IOMAP_REPORT) { if (flags & IOMAP_REPORT) {
if (ext4_has_inline_data(inode)) { if (ext4_has_inline_data(inode)) {
...@@ -3948,6 +3952,7 @@ static const struct address_space_operations ext4_dax_aops = { ...@@ -3948,6 +3952,7 @@ static const struct address_space_operations ext4_dax_aops = {
.writepages = ext4_dax_writepages, .writepages = ext4_dax_writepages,
.direct_IO = noop_direct_IO, .direct_IO = noop_direct_IO,
.set_page_dirty = noop_set_page_dirty, .set_page_dirty = noop_set_page_dirty,
.bmap = ext4_bmap,
.invalidatepage = noop_invalidatepage, .invalidatepage = noop_invalidatepage,
}; };
...@@ -4192,9 +4197,8 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset, ...@@ -4192,9 +4197,8 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
return 0; return 0;
} }
static void ext4_wait_dax_page(struct ext4_inode_info *ei, bool *did_unlock) static void ext4_wait_dax_page(struct ext4_inode_info *ei)
{ {
*did_unlock = true;
up_write(&ei->i_mmap_sem); up_write(&ei->i_mmap_sem);
schedule(); schedule();
down_write(&ei->i_mmap_sem); down_write(&ei->i_mmap_sem);
...@@ -4204,14 +4208,12 @@ int ext4_break_layouts(struct inode *inode) ...@@ -4204,14 +4208,12 @@ int ext4_break_layouts(struct inode *inode)
{ {
struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_inode_info *ei = EXT4_I(inode);
struct page *page; struct page *page;
bool retry;
int error; int error;
if (WARN_ON_ONCE(!rwsem_is_locked(&ei->i_mmap_sem))) if (WARN_ON_ONCE(!rwsem_is_locked(&ei->i_mmap_sem)))
return -EINVAL; return -EINVAL;
do { do {
retry = false;
page = dax_layout_busy_page(inode->i_mapping); page = dax_layout_busy_page(inode->i_mapping);
if (!page) if (!page)
return 0; return 0;
...@@ -4219,8 +4221,8 @@ int ext4_break_layouts(struct inode *inode) ...@@ -4219,8 +4221,8 @@ int ext4_break_layouts(struct inode *inode)
error = ___wait_var_event(&page->_refcount, error = ___wait_var_event(&page->_refcount,
atomic_read(&page->_refcount) == 1, atomic_read(&page->_refcount) == 1,
TASK_INTERRUPTIBLE, 0, 0, TASK_INTERRUPTIBLE, 0, 0,
ext4_wait_dax_page(ei, &retry)); ext4_wait_dax_page(ei));
} while (error == 0 && retry); } while (error == 0);
return error; return error;
} }
...@@ -4895,6 +4897,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ...@@ -4895,6 +4897,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
* not initialized on a new filesystem. */ * not initialized on a new filesystem. */
} }
ei->i_flags = le32_to_cpu(raw_inode->i_flags); ei->i_flags = le32_to_cpu(raw_inode->i_flags);
ext4_set_inode_flags(inode);
inode->i_blocks = ext4_inode_blocks(raw_inode, ei); inode->i_blocks = ext4_inode_blocks(raw_inode, ei);
ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo); ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo);
if (ext4_has_feature_64bit(sb)) if (ext4_has_feature_64bit(sb))
...@@ -5041,7 +5044,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ...@@ -5041,7 +5044,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
goto bad_inode; goto bad_inode;
} }
brelse(iloc.bh); brelse(iloc.bh);
ext4_set_inode_flags(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
return inode; return inode;
......
...@@ -49,7 +49,6 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh) ...@@ -49,7 +49,6 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
*/ */
sb_start_write(sb); sb_start_write(sb);
ext4_mmp_csum_set(sb, mmp); ext4_mmp_csum_set(sb, mmp);
mark_buffer_dirty(bh);
lock_buffer(bh); lock_buffer(bh);
bh->b_end_io = end_buffer_write_sync; bh->b_end_io = end_buffer_write_sync;
get_bh(bh); get_bh(bh);
......
...@@ -3478,6 +3478,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -3478,6 +3478,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
int credits; int credits;
u8 old_file_type; u8 old_file_type;
if (new.inode && new.inode->i_nlink == 0) {
EXT4_ERROR_INODE(new.inode,
"target of rename is already freed");
return -EFSCORRUPTED;
}
if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) && if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) &&
(!projid_eq(EXT4_I(new_dir)->i_projid, (!projid_eq(EXT4_I(new_dir)->i_projid,
EXT4_I(old_dentry->d_inode)->i_projid))) EXT4_I(old_dentry->d_inode)->i_projid)))
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
int ext4_resize_begin(struct super_block *sb) int ext4_resize_begin(struct super_block *sb)
{ {
struct ext4_sb_info *sbi = EXT4_SB(sb);
int ret = 0; int ret = 0;
if (!capable(CAP_SYS_RESOURCE)) if (!capable(CAP_SYS_RESOURCE))
...@@ -29,7 +30,7 @@ int ext4_resize_begin(struct super_block *sb) ...@@ -29,7 +30,7 @@ int ext4_resize_begin(struct super_block *sb)
* because the user tools have no way of handling this. Probably a * because the user tools have no way of handling this. Probably a
* bad time to do it anyways. * bad time to do it anyways.
*/ */
if (EXT4_SB(sb)->s_sbh->b_blocknr != if (EXT4_B2C(sbi, sbi->s_sbh->b_blocknr) !=
le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) { le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) {
ext4_warning(sb, "won't resize using backup superblock at %llu", ext4_warning(sb, "won't resize using backup superblock at %llu",
(unsigned long long)EXT4_SB(sb)->s_sbh->b_blocknr); (unsigned long long)EXT4_SB(sb)->s_sbh->b_blocknr);
...@@ -1986,6 +1987,26 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) ...@@ -1986,6 +1987,26 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
} }
} }
/*
* Make sure the last group has enough space so that it's
* guaranteed to have enough space for all metadata blocks
* that it might need to hold. (We might not need to store
* the inode table blocks in the last block group, but there
* will be cases where this might be needed.)
*/
if ((ext4_group_first_block_no(sb, n_group) +
ext4_group_overhead_blocks(sb, n_group) + 2 +
sbi->s_itb_per_group + sbi->s_cluster_ratio) >= n_blocks_count) {
n_blocks_count = ext4_group_first_block_no(sb, n_group);
n_group--;
n_blocks_count_retry = 0;
if (resize_inode) {
iput(resize_inode);
resize_inode = NULL;
}
goto retry;
}
/* extend the last group */ /* extend the last group */
if (n_group == o_group) if (n_group == o_group)
add = n_blocks_count - o_blocks_count; add = n_blocks_count - o_blocks_count;
......
...@@ -2145,6 +2145,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb, ...@@ -2145,6 +2145,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb); SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
if (test_opt(sb, DATA_ERR_ABORT)) if (test_opt(sb, DATA_ERR_ABORT))
SEQ_OPTS_PUTS("data_err=abort"); SEQ_OPTS_PUTS("data_err=abort");
if (DUMMY_ENCRYPTION_ENABLED(sbi))
SEQ_OPTS_PUTS("test_dummy_encryption");
ext4_show_quota_options(seq, sb); ext4_show_quota_options(seq, sb);
return 0; return 0;
...@@ -4378,11 +4380,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -4378,11 +4380,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
block = ext4_count_free_clusters(sb); block = ext4_count_free_clusters(sb);
ext4_free_blocks_count_set(sbi->s_es, ext4_free_blocks_count_set(sbi->s_es,
EXT4_C2B(sbi, block)); EXT4_C2B(sbi, block));
ext4_superblock_csum_set(sb);
err = percpu_counter_init(&sbi->s_freeclusters_counter, block, err = percpu_counter_init(&sbi->s_freeclusters_counter, block,
GFP_KERNEL); GFP_KERNEL);
if (!err) { if (!err) {
unsigned long freei = ext4_count_free_inodes(sb); unsigned long freei = ext4_count_free_inodes(sb);
sbi->s_es->s_free_inodes_count = cpu_to_le32(freei); sbi->s_es->s_free_inodes_count = cpu_to_le32(freei);
ext4_superblock_csum_set(sb);
err = percpu_counter_init(&sbi->s_freeinodes_counter, freei, err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,
GFP_KERNEL); GFP_KERNEL);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment