Commit 6a8098a4 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull ext4 fixes from Ted Ts'o:
 "Fix a number of ext4 bugs; the most serious of which is a bug in the
  lazytime mount optimization code where we could end up updating the
  timestamps to the wrong inode"

* tag 'for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: fix an ext3 collapse range regression in xfstests
  jbd2: fix r_count overflows leading to buffer overflow in journal recovery
  ext4: check for zero length extent explicitly
  ext4: fix NULL pointer dereference when journal restart fails
  ext4: remove unused function prototype from ext4.h
  ext4: don't save the error information if the block device is read-only
  ext4: fix lazytime optimization
parents c7309e88 b9576fc3
...@@ -2889,7 +2889,6 @@ extern int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -2889,7 +2889,6 @@ extern int ext4_map_blocks(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map, int flags); struct ext4_map_blocks *map, int flags);
extern int ext4_ext_calc_metadata_amount(struct inode *inode, extern int ext4_ext_calc_metadata_amount(struct inode *inode,
ext4_lblk_t lblocks); ext4_lblk_t lblocks);
extern int ext4_extent_tree_init(handle_t *, struct inode *);
extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode, extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
int num, int num,
struct ext4_ext_path *path); struct ext4_ext_path *path);
......
...@@ -87,6 +87,12 @@ int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) ...@@ -87,6 +87,12 @@ int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
ext4_put_nojournal(handle); ext4_put_nojournal(handle);
return 0; return 0;
} }
if (!handle->h_transaction) {
err = jbd2_journal_stop(handle);
return handle->h_err ? handle->h_err : err;
}
sb = handle->h_transaction->t_journal->j_private; sb = handle->h_transaction->t_journal->j_private;
err = handle->h_err; err = handle->h_err;
rc = jbd2_journal_stop(handle); rc = jbd2_journal_stop(handle);
......
...@@ -377,7 +377,7 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext) ...@@ -377,7 +377,7 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
ext4_lblk_t lblock = le32_to_cpu(ext->ee_block); ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
ext4_lblk_t last = lblock + len - 1; ext4_lblk_t last = lblock + len - 1;
if (lblock > last) if (len == 0 || lblock > last)
return 0; return 0;
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len); return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
} }
...@@ -5396,6 +5396,14 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) ...@@ -5396,6 +5396,14 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
loff_t new_size, ioffset; loff_t new_size, ioffset;
int ret; int ret;
/*
* We need to test this early because xfstests assumes that a
* collapse range of (0, 1) will return EOPNOTSUPP if the file
* system does not support collapse range.
*/
if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
return -EOPNOTSUPP;
/* Collapse range works only on fs block size aligned offsets. */ /* Collapse range works only on fs block size aligned offsets. */
if (offset & (EXT4_CLUSTER_SIZE(sb) - 1) || if (offset & (EXT4_CLUSTER_SIZE(sb) - 1) ||
len & (EXT4_CLUSTER_SIZE(sb) - 1)) len & (EXT4_CLUSTER_SIZE(sb) - 1))
......
...@@ -4345,7 +4345,7 @@ static void ext4_update_other_inodes_time(struct super_block *sb, ...@@ -4345,7 +4345,7 @@ static void ext4_update_other_inodes_time(struct super_block *sb,
int inode_size = EXT4_INODE_SIZE(sb); int inode_size = EXT4_INODE_SIZE(sb);
oi.orig_ino = orig_ino; oi.orig_ino = orig_ino;
ino = orig_ino & ~(inodes_per_block - 1); ino = (orig_ino & ~(inodes_per_block - 1)) + 1;
for (i = 0; i < inodes_per_block; i++, ino++, buf += inode_size) { for (i = 0; i < inodes_per_block; i++, ino++, buf += inode_size) {
if (ino == orig_ino) if (ino == orig_ino)
continue; continue;
......
...@@ -294,6 +294,8 @@ static void __save_error_info(struct super_block *sb, const char *func, ...@@ -294,6 +294,8 @@ static void __save_error_info(struct super_block *sb, const char *func,
struct ext4_super_block *es = EXT4_SB(sb)->s_es; struct ext4_super_block *es = EXT4_SB(sb)->s_es;
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS; EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
if (bdev_read_only(sb->s_bdev))
return;
es->s_state |= cpu_to_le16(EXT4_ERROR_FS); es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
es->s_last_error_time = cpu_to_le32(get_seconds()); es->s_last_error_time = cpu_to_le32(get_seconds());
strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func)); strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func));
......
...@@ -842,15 +842,23 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, ...@@ -842,15 +842,23 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
{ {
jbd2_journal_revoke_header_t *header; jbd2_journal_revoke_header_t *header;
int offset, max; int offset, max;
int csum_size = 0;
__u32 rcount;
int record_len = 4; int record_len = 4;
header = (jbd2_journal_revoke_header_t *) bh->b_data; header = (jbd2_journal_revoke_header_t *) bh->b_data;
offset = sizeof(jbd2_journal_revoke_header_t); offset = sizeof(jbd2_journal_revoke_header_t);
max = be32_to_cpu(header->r_count); rcount = be32_to_cpu(header->r_count);
if (!jbd2_revoke_block_csum_verify(journal, header)) if (!jbd2_revoke_block_csum_verify(journal, header))
return -EINVAL; return -EINVAL;
if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_revoke_tail);
if (rcount > journal->j_blocksize - csum_size)
return -EINVAL;
max = rcount;
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
record_len = 8; record_len = 8;
......
...@@ -577,7 +577,7 @@ static void write_one_revoke_record(journal_t *journal, ...@@ -577,7 +577,7 @@ static void write_one_revoke_record(journal_t *journal,
{ {
int csum_size = 0; int csum_size = 0;
struct buffer_head *descriptor; struct buffer_head *descriptor;
int offset; int sz, offset;
journal_header_t *header; journal_header_t *header;
/* If we are already aborting, this all becomes a noop. We /* If we are already aborting, this all becomes a noop. We
...@@ -594,9 +594,14 @@ static void write_one_revoke_record(journal_t *journal, ...@@ -594,9 +594,14 @@ static void write_one_revoke_record(journal_t *journal,
if (jbd2_journal_has_csum_v2or3(journal)) if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_revoke_tail); csum_size = sizeof(struct jbd2_journal_revoke_tail);
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
sz = 8;
else
sz = 4;
/* Make sure we have a descriptor with space left for the record */ /* Make sure we have a descriptor with space left for the record */
if (descriptor) { if (descriptor) {
if (offset >= journal->j_blocksize - csum_size) { if (offset + sz > journal->j_blocksize - csum_size) {
flush_descriptor(journal, descriptor, offset, write_op); flush_descriptor(journal, descriptor, offset, write_op);
descriptor = NULL; descriptor = NULL;
} }
...@@ -619,16 +624,13 @@ static void write_one_revoke_record(journal_t *journal, ...@@ -619,16 +624,13 @@ static void write_one_revoke_record(journal_t *journal,
*descriptorp = descriptor; *descriptorp = descriptor;
} }
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) { if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
* ((__be64 *)(&descriptor->b_data[offset])) = * ((__be64 *)(&descriptor->b_data[offset])) =
cpu_to_be64(record->blocknr); cpu_to_be64(record->blocknr);
offset += 8; else
} else {
* ((__be32 *)(&descriptor->b_data[offset])) = * ((__be32 *)(&descriptor->b_data[offset])) =
cpu_to_be32(record->blocknr); cpu_to_be32(record->blocknr);
offset += 4; offset += sz;
}
*offsetp = offset; *offsetp = offset;
} }
......
...@@ -551,7 +551,6 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) ...@@ -551,7 +551,6 @@ int jbd2_journal_extend(handle_t *handle, int nblocks)
int result; int result;
int wanted; int wanted;
WARN_ON(!transaction);
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
return -EROFS; return -EROFS;
journal = transaction->t_journal; journal = transaction->t_journal;
...@@ -627,7 +626,6 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask) ...@@ -627,7 +626,6 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask)
tid_t tid; tid_t tid;
int need_to_start, ret; int need_to_start, ret;
WARN_ON(!transaction);
/* If we've had an abort of any type, don't even think about /* If we've had an abort of any type, don't even think about
* actually doing the restart! */ * actually doing the restart! */
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
...@@ -785,7 +783,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, ...@@ -785,7 +783,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh,
int need_copy = 0; int need_copy = 0;
unsigned long start_lock, time_lock; unsigned long start_lock, time_lock;
WARN_ON(!transaction);
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
return -EROFS; return -EROFS;
journal = transaction->t_journal; journal = transaction->t_journal;
...@@ -1051,7 +1048,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) ...@@ -1051,7 +1048,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
int err; int err;
jbd_debug(5, "journal_head %p\n", jh); jbd_debug(5, "journal_head %p\n", jh);
WARN_ON(!transaction);
err = -EROFS; err = -EROFS;
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
goto out; goto out;
...@@ -1266,7 +1262,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) ...@@ -1266,7 +1262,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
struct journal_head *jh; struct journal_head *jh;
int ret = 0; int ret = 0;
WARN_ON(!transaction);
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
return -EROFS; return -EROFS;
journal = transaction->t_journal; journal = transaction->t_journal;
...@@ -1397,7 +1392,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) ...@@ -1397,7 +1392,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
int err = 0; int err = 0;
int was_modified = 0; int was_modified = 0;
WARN_ON(!transaction);
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
return -EROFS; return -EROFS;
journal = transaction->t_journal; journal = transaction->t_journal;
...@@ -1530,8 +1524,22 @@ int jbd2_journal_stop(handle_t *handle) ...@@ -1530,8 +1524,22 @@ int jbd2_journal_stop(handle_t *handle)
tid_t tid; tid_t tid;
pid_t pid; pid_t pid;
if (!transaction) if (!transaction) {
/*
* Handle is already detached from the transaction so
* there is nothing to do other than decrease a refcount,
* or free the handle if refcount drops to zero
*/
if (--handle->h_ref > 0) {
jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1,
handle->h_ref);
return err;
} else {
if (handle->h_rsv_handle)
jbd2_free_handle(handle->h_rsv_handle);
goto free_and_exit; goto free_and_exit;
}
}
journal = transaction->t_journal; journal = transaction->t_journal;
J_ASSERT(journal_current_handle() == handle); J_ASSERT(journal_current_handle() == handle);
...@@ -2373,7 +2381,6 @@ int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode) ...@@ -2373,7 +2381,6 @@ int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode)
transaction_t *transaction = handle->h_transaction; transaction_t *transaction = handle->h_transaction;
journal_t *journal; journal_t *journal;
WARN_ON(!transaction);
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
return -EROFS; return -EROFS;
journal = transaction->t_journal; journal = transaction->t_journal;
......
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