Commit feaf2229 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull ext4 fixes from Ted Ts'o:
 "Ext4 bug fixes.

  We also reserved code points for encryption and read-only images (for
  which the implementation is mostly just the reserved code point for a
  read-only feature :-)"

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: fix indirect punch hole corruption
  ext4: ignore journal checksum on remount; don't fail
  ext4: remove duplicate remount check for JOURNAL_CHECKSUM change
  ext4: fix mmap data corruption in nodelalloc mode when blocksize < pagesize
  ext4: support read-only images
  ext4: change to use setup_timer() instead of init_timer()
  ext4: reserve codepoints used by the ext4 encryption feature
  jbd2: complain about descriptor block checksum errors
parents be5e6616 6f30b7e3
...@@ -364,7 +364,8 @@ struct flex_groups { ...@@ -364,7 +364,8 @@ struct flex_groups {
#define EXT4_DIRTY_FL 0x00000100 #define EXT4_DIRTY_FL 0x00000100
#define EXT4_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ #define EXT4_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
#define EXT4_NOCOMPR_FL 0x00000400 /* Don't compress */ #define EXT4_NOCOMPR_FL 0x00000400 /* Don't compress */
#define EXT4_ECOMPR_FL 0x00000800 /* Compression error */ /* nb: was previously EXT2_ECOMPR_FL */
#define EXT4_ENCRYPT_FL 0x00000800 /* encrypted file */
/* End compression flags --- maybe not all used */ /* End compression flags --- maybe not all used */
#define EXT4_INDEX_FL 0x00001000 /* hash-indexed directory */ #define EXT4_INDEX_FL 0x00001000 /* hash-indexed directory */
#define EXT4_IMAGIC_FL 0x00002000 /* AFS directory */ #define EXT4_IMAGIC_FL 0x00002000 /* AFS directory */
...@@ -421,7 +422,7 @@ enum { ...@@ -421,7 +422,7 @@ enum {
EXT4_INODE_DIRTY = 8, EXT4_INODE_DIRTY = 8,
EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */ EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */
EXT4_INODE_NOCOMPR = 10, /* Don't compress */ EXT4_INODE_NOCOMPR = 10, /* Don't compress */
EXT4_INODE_ECOMPR = 11, /* Compression error */ EXT4_INODE_ENCRYPT = 11, /* Compression error */
/* End compression flags --- maybe not all used */ /* End compression flags --- maybe not all used */
EXT4_INODE_INDEX = 12, /* hash-indexed directory */ EXT4_INODE_INDEX = 12, /* hash-indexed directory */
EXT4_INODE_IMAGIC = 13, /* AFS directory */ EXT4_INODE_IMAGIC = 13, /* AFS directory */
...@@ -466,7 +467,7 @@ static inline void ext4_check_flag_values(void) ...@@ -466,7 +467,7 @@ static inline void ext4_check_flag_values(void)
CHECK_FLAG_VALUE(DIRTY); CHECK_FLAG_VALUE(DIRTY);
CHECK_FLAG_VALUE(COMPRBLK); CHECK_FLAG_VALUE(COMPRBLK);
CHECK_FLAG_VALUE(NOCOMPR); CHECK_FLAG_VALUE(NOCOMPR);
CHECK_FLAG_VALUE(ECOMPR); CHECK_FLAG_VALUE(ENCRYPT);
CHECK_FLAG_VALUE(INDEX); CHECK_FLAG_VALUE(INDEX);
CHECK_FLAG_VALUE(IMAGIC); CHECK_FLAG_VALUE(IMAGIC);
CHECK_FLAG_VALUE(JOURNAL_DATA); CHECK_FLAG_VALUE(JOURNAL_DATA);
...@@ -1048,6 +1049,12 @@ extern void ext4_set_bits(void *bm, int cur, int len); ...@@ -1048,6 +1049,12 @@ extern void ext4_set_bits(void *bm, int cur, int len);
/* Metadata checksum algorithm codes */ /* Metadata checksum algorithm codes */
#define EXT4_CRC32C_CHKSUM 1 #define EXT4_CRC32C_CHKSUM 1
/* Encryption algorithms */
#define EXT4_ENCRYPTION_MODE_INVALID 0
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
/* /*
* Structure of the super block * Structure of the super block
*/ */
...@@ -1161,7 +1168,8 @@ struct ext4_super_block { ...@@ -1161,7 +1168,8 @@ struct ext4_super_block {
__le32 s_grp_quota_inum; /* inode for tracking group quota */ __le32 s_grp_quota_inum; /* inode for tracking group quota */
__le32 s_overhead_clusters; /* overhead blocks/clusters in fs */ __le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
__le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */ __le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */
__le32 s_reserved[106]; /* Padding to the end of the block */ __u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
__le32 s_reserved[105]; /* Padding to the end of the block */
__le32 s_checksum; /* crc32c(superblock) */ __le32 s_checksum; /* crc32c(superblock) */
}; };
...@@ -1527,6 +1535,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) ...@@ -1527,6 +1535,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
* GDT_CSUM bits are mutually exclusive. * GDT_CSUM bits are mutually exclusive.
*/ */
#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000
#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
...@@ -1542,6 +1551,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) ...@@ -1542,6 +1551,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
#define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */ #define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */
#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ #define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */ #define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR #define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ #define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
......
...@@ -1401,10 +1401,7 @@ int ext4_ind_remove_space(handle_t *handle, struct inode *inode, ...@@ -1401,10 +1401,7 @@ int ext4_ind_remove_space(handle_t *handle, struct inode *inode,
* to free. Everything was covered by the start * to free. Everything was covered by the start
* of the range. * of the range.
*/ */
return 0; goto do_indirects;
} else {
/* Shared branch grows from an indirect block */
partial2--;
} }
} else { } else {
/* /*
...@@ -1435,56 +1432,96 @@ int ext4_ind_remove_space(handle_t *handle, struct inode *inode, ...@@ -1435,56 +1432,96 @@ int ext4_ind_remove_space(handle_t *handle, struct inode *inode,
/* Punch happened within the same level (n == n2) */ /* Punch happened within the same level (n == n2) */
partial = ext4_find_shared(inode, n, offsets, chain, &nr); partial = ext4_find_shared(inode, n, offsets, chain, &nr);
partial2 = ext4_find_shared(inode, n2, offsets2, chain2, &nr2); partial2 = ext4_find_shared(inode, n2, offsets2, chain2, &nr2);
/*
* ext4_find_shared returns Indirect structure which /* Free top, but only if partial2 isn't its subtree. */
* points to the last element which should not be if (nr) {
* removed by truncate. But this is end of the range int level = min(partial - chain, partial2 - chain2);
* in punch_hole so we need to point to the next element int i;
*/ int subtree = 1;
partial2->p++;
while ((partial > chain) || (partial2 > chain2)) { for (i = 0; i <= level; i++) {
/* We're at the same block, so we're almost finished */ if (offsets[i] != offsets2[i]) {
if ((partial->bh && partial2->bh) && subtree = 0;
(partial->bh->b_blocknr == partial2->bh->b_blocknr)) { break;
if ((partial > chain) && (partial2 > chain2)) { }
}
if (!subtree) {
if (partial == chain) {
/* Shared branch grows from the inode */
ext4_free_branches(handle, inode, NULL,
&nr, &nr+1,
(chain+n-1) - partial);
*partial->p = 0;
} else {
/* Shared branch grows from an indirect block */
BUFFER_TRACE(partial->bh, "get_write_access");
ext4_free_branches(handle, inode, partial->bh, ext4_free_branches(handle, inode, partial->bh,
partial->p + 1, partial->p,
partial2->p, partial->p+1,
(chain+n-1) - partial); (chain+n-1) - partial);
BUFFER_TRACE(partial->bh, "call brelse");
brelse(partial->bh);
BUFFER_TRACE(partial2->bh, "call brelse");
brelse(partial2->bh);
} }
return 0;
} }
}
if (!nr2) {
/* /*
* Clear the ends of indirect blocks on the shared branch * ext4_find_shared returns Indirect structure which
* at the start of the range * points to the last element which should not be
* removed by truncate. But this is end of the range
* in punch_hole so we need to point to the next element
*/ */
if (partial > chain) { partial2->p++;
}
while (partial > chain || partial2 > chain2) {
int depth = (chain+n-1) - partial;
int depth2 = (chain2+n2-1) - partial2;
if (partial > chain && partial2 > chain2 &&
partial->bh->b_blocknr == partial2->bh->b_blocknr) {
/*
* We've converged on the same block. Clear the range,
* then we're done.
*/
ext4_free_branches(handle, inode, partial->bh, ext4_free_branches(handle, inode, partial->bh,
partial->p + 1, partial->p + 1,
(__le32 *)partial->bh->b_data+addr_per_block, partial2->p,
(chain+n-1) - partial); (chain+n-1) - partial);
BUFFER_TRACE(partial->bh, "call brelse"); BUFFER_TRACE(partial->bh, "call brelse");
brelse(partial->bh); brelse(partial->bh);
partial--; BUFFER_TRACE(partial2->bh, "call brelse");
brelse(partial2->bh);
return 0;
} }
/* /*
* Clear the ends of indirect blocks on the shared branch * The start and end partial branches may not be at the same
* at the end of the range * level even though the punch happened within one level. So, we
* give them a chance to arrive at the same level, then walk
* them in step with each other until we converge on the same
* block.
*/ */
if (partial2 > chain2) { if (partial > chain && depth <= depth2) {
ext4_free_branches(handle, inode, partial->bh,
partial->p + 1,
(__le32 *)partial->bh->b_data+addr_per_block,
(chain+n-1) - partial);
BUFFER_TRACE(partial->bh, "call brelse");
brelse(partial->bh);
partial--;
}
if (partial2 > chain2 && depth2 <= depth) {
ext4_free_branches(handle, inode, partial2->bh, ext4_free_branches(handle, inode, partial2->bh,
(__le32 *)partial2->bh->b_data, (__le32 *)partial2->bh->b_data,
partial2->p, partial2->p,
(chain2+n-1) - partial2); (chain2+n2-1) - partial2);
BUFFER_TRACE(partial2->bh, "call brelse"); BUFFER_TRACE(partial2->bh, "call brelse");
brelse(partial2->bh); brelse(partial2->bh);
partial2--; partial2--;
} }
} }
return 0;
do_indirects: do_indirects:
/* Kill the remaining (whole) subtrees */ /* Kill the remaining (whole) subtrees */
......
...@@ -1024,6 +1024,7 @@ static int ext4_write_end(struct file *file, ...@@ -1024,6 +1024,7 @@ static int ext4_write_end(struct file *file,
{ {
handle_t *handle = ext4_journal_current_handle(); handle_t *handle = ext4_journal_current_handle();
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
loff_t old_size = inode->i_size;
int ret = 0, ret2; int ret = 0, ret2;
int i_size_changed = 0; int i_size_changed = 0;
...@@ -1054,6 +1055,8 @@ static int ext4_write_end(struct file *file, ...@@ -1054,6 +1055,8 @@ static int ext4_write_end(struct file *file,
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
if (old_size < pos)
pagecache_isize_extended(inode, old_size, pos);
/* /*
* Don't mark the inode dirty under page lock. First, it unnecessarily * Don't mark the inode dirty under page lock. First, it unnecessarily
* makes the holding time of page lock longer. Second, it forces lock * makes the holding time of page lock longer. Second, it forces lock
...@@ -1095,6 +1098,7 @@ static int ext4_journalled_write_end(struct file *file, ...@@ -1095,6 +1098,7 @@ static int ext4_journalled_write_end(struct file *file,
{ {
handle_t *handle = ext4_journal_current_handle(); handle_t *handle = ext4_journal_current_handle();
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
loff_t old_size = inode->i_size;
int ret = 0, ret2; int ret = 0, ret2;
int partial = 0; int partial = 0;
unsigned from, to; unsigned from, to;
...@@ -1127,6 +1131,9 @@ static int ext4_journalled_write_end(struct file *file, ...@@ -1127,6 +1131,9 @@ static int ext4_journalled_write_end(struct file *file,
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
if (old_size < pos)
pagecache_isize_extended(inode, old_size, pos);
if (size_changed) { if (size_changed) {
ret2 = ext4_mark_inode_dirty(handle, inode); ret2 = ext4_mark_inode_dirty(handle, inode);
if (!ret) if (!ret)
......
...@@ -2779,6 +2779,12 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly) ...@@ -2779,6 +2779,12 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
if (readonly) if (readonly)
return 1; return 1;
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_READONLY)) {
ext4_msg(sb, KERN_INFO, "filesystem is read-only");
sb->s_flags |= MS_RDONLY;
return 1;
}
/* Check that feature set is OK for a read-write mount */ /* Check that feature set is OK for a read-write mount */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT4_FEATURE_RO_COMPAT_SUPP)) { if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT4_FEATURE_RO_COMPAT_SUPP)) {
ext4_msg(sb, KERN_ERR, "couldn't mount RDWR because of " ext4_msg(sb, KERN_ERR, "couldn't mount RDWR because of "
...@@ -3936,9 +3942,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3936,9 +3942,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
get_random_bytes(&sbi->s_next_generation, sizeof(u32)); get_random_bytes(&sbi->s_next_generation, sizeof(u32));
spin_lock_init(&sbi->s_next_gen_lock); spin_lock_init(&sbi->s_next_gen_lock);
init_timer(&sbi->s_err_report); setup_timer(&sbi->s_err_report, print_daily_error_info,
sbi->s_err_report.function = print_daily_error_info; (unsigned long) sb);
sbi->s_err_report.data = (unsigned long) sb;
/* Register extent status tree shrinker */ /* Register extent status tree shrinker */
if (ext4_es_register_shrinker(sbi)) if (ext4_es_register_shrinker(sbi))
...@@ -4866,9 +4871,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -4866,9 +4871,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_journal && sbi->s_journal->j_task->io_context) if (sbi->s_journal && sbi->s_journal->j_task->io_context)
journal_ioprio = sbi->s_journal->j_task->io_context->ioprio; journal_ioprio = sbi->s_journal->j_task->io_context->ioprio;
/*
* Allow the "check" option to be passed as a remount option.
*/
if (!parse_options(data, sb, NULL, &journal_ioprio, 1)) { if (!parse_options(data, sb, NULL, &journal_ioprio, 1)) {
err = -EINVAL; err = -EINVAL;
goto restore_opts; goto restore_opts;
...@@ -4877,17 +4879,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -4877,17 +4879,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^ if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
test_opt(sb, JOURNAL_CHECKSUM)) { test_opt(sb, JOURNAL_CHECKSUM)) {
ext4_msg(sb, KERN_ERR, "changing journal_checksum " ext4_msg(sb, KERN_ERR, "changing journal_checksum "
"during remount not supported"); "during remount not supported; ignoring");
err = -EINVAL; sbi->s_mount_opt ^= EXT4_MOUNT_JOURNAL_CHECKSUM;
goto restore_opts;
}
if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
test_opt(sb, JOURNAL_CHECKSUM)) {
ext4_msg(sb, KERN_ERR, "changing journal_checksum "
"during remount not supported");
err = -EINVAL;
goto restore_opts;
} }
if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
...@@ -4963,7 +4956,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -4963,7 +4956,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
ext4_mark_recovery_complete(sb, es); ext4_mark_recovery_complete(sb, es);
} else { } else {
/* Make sure we can mount this feature set readwrite */ /* Make sure we can mount this feature set readwrite */
if (!ext4_feature_set_ok(sb, 0)) { if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_READONLY) ||
!ext4_feature_set_ok(sb, 0)) {
err = -EROFS; err = -EROFS;
goto restore_opts; goto restore_opts;
} }
......
...@@ -524,6 +524,9 @@ static int do_one_pass(journal_t *journal, ...@@ -524,6 +524,9 @@ static int do_one_pass(journal_t *journal,
if (descr_csum_size > 0 && if (descr_csum_size > 0 &&
!jbd2_descr_block_csum_verify(journal, !jbd2_descr_block_csum_verify(journal,
bh->b_data)) { bh->b_data)) {
printk(KERN_ERR "JBD2: Invalid checksum "
"recovering block %lu in log\n",
next_log_block);
err = -EIO; err = -EIO;
brelse(bh); brelse(bh);
goto failed; goto failed;
......
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