Skip to content

Commit

Permalink
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kern…
Browse files Browse the repository at this point in the history
…el/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
  • Loading branch information
torvalds committed Feb 23, 2015
2 parents be5e661 + 6f30b7e commit feaf222
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 56 deletions.
18 changes: 14 additions & 4 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,8 @@ struct flex_groups {
#define EXT4_DIRTY_FL 0x00000100
#define EXT4_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
#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 */
#define EXT4_INDEX_FL 0x00001000 /* hash-indexed directory */
#define EXT4_IMAGIC_FL 0x00002000 /* AFS directory */
Expand Down Expand Up @@ -421,7 +422,7 @@ enum {
EXT4_INODE_DIRTY = 8,
EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */
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 */
EXT4_INODE_INDEX = 12, /* hash-indexed directory */
EXT4_INODE_IMAGIC = 13, /* AFS directory */
Expand Down Expand Up @@ -466,7 +467,7 @@ static inline void ext4_check_flag_values(void)
CHECK_FLAG_VALUE(DIRTY);
CHECK_FLAG_VALUE(COMPRBLK);
CHECK_FLAG_VALUE(NOCOMPR);
CHECK_FLAG_VALUE(ECOMPR);
CHECK_FLAG_VALUE(ENCRYPT);
CHECK_FLAG_VALUE(INDEX);
CHECK_FLAG_VALUE(IMAGIC);
CHECK_FLAG_VALUE(JOURNAL_DATA);
Expand Down Expand Up @@ -1048,6 +1049,12 @@ extern void ext4_set_bits(void *bm, int cur, int len);
/* Metadata checksum algorithm codes */
#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
*/
Expand Down Expand Up @@ -1161,7 +1168,8 @@ struct ext4_super_block {
__le32 s_grp_quota_inum; /* inode for tracking group quota */
__le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
__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) */
};

Expand Down Expand Up @@ -1527,6 +1535,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
* GDT_CSUM bits are mutually exclusive.
*/
#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_FILETYPE 0x0002
Expand All @@ -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_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
#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_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
Expand Down
105 changes: 71 additions & 34 deletions fs/ext4/indirect.c
Original file line number Diff line number Diff line change
Expand Up @@ -1401,10 +1401,7 @@ int ext4_ind_remove_space(handle_t *handle, struct inode *inode,
* to free. Everything was covered by the start
* of the range.
*/
return 0;
} else {
/* Shared branch grows from an indirect block */
partial2--;
goto do_indirects;
}
} else {
/*
Expand Down Expand Up @@ -1435,56 +1432,96 @@ int ext4_ind_remove_space(handle_t *handle, struct inode *inode,
/* Punch happened within the same level (n == n2) */
partial = ext4_find_shared(inode, n, offsets, chain, &nr);
partial2 = ext4_find_shared(inode, n2, offsets2, chain2, &nr2);
/*
* ext4_find_shared returns Indirect structure which
* 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
*/
partial2->p++;
while ((partial > chain) || (partial2 > chain2)) {
/* We're at the same block, so we're almost finished */
if ((partial->bh && partial2->bh) &&
(partial->bh->b_blocknr == partial2->bh->b_blocknr)) {
if ((partial > chain) && (partial2 > chain2)) {

/* Free top, but only if partial2 isn't its subtree. */
if (nr) {
int level = min(partial - chain, partial2 - chain2);
int i;
int subtree = 1;

for (i = 0; i <= level; i++) {
if (offsets[i] != offsets2[i]) {
subtree = 0;
break;
}
}

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,
partial->p + 1,
partial2->p,
partial->p,
partial->p+1,
(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
* at the start of the range
* ext4_find_shared returns Indirect structure which
* 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,
partial->p + 1,
(__le32 *)partial->bh->b_data+addr_per_block,
(chain+n-1) - partial);
partial->p + 1,
partial2->p,
(chain+n-1) - partial);
BUFFER_TRACE(partial->bh, "call brelse");
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
* at the end of the range
* The start and end partial branches may not be at the same
* 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,
(__le32 *)partial2->bh->b_data,
partial2->p,
(chain2+n-1) - partial2);
(chain2+n2-1) - partial2);
BUFFER_TRACE(partial2->bh, "call brelse");
brelse(partial2->bh);
partial2--;
}
}
return 0;

do_indirects:
/* Kill the remaining (whole) subtrees */
Expand Down
7 changes: 7 additions & 0 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,7 @@ static int ext4_write_end(struct file *file,
{
handle_t *handle = ext4_journal_current_handle();
struct inode *inode = mapping->host;
loff_t old_size = inode->i_size;
int ret = 0, ret2;
int i_size_changed = 0;

Expand Down Expand Up @@ -1054,6 +1055,8 @@ static int ext4_write_end(struct file *file,
unlock_page(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
* makes the holding time of page lock longer. Second, it forces lock
Expand Down Expand Up @@ -1095,6 +1098,7 @@ static int ext4_journalled_write_end(struct file *file,
{
handle_t *handle = ext4_journal_current_handle();
struct inode *inode = mapping->host;
loff_t old_size = inode->i_size;
int ret = 0, ret2;
int partial = 0;
unsigned from, to;
Expand Down Expand Up @@ -1127,6 +1131,9 @@ static int ext4_journalled_write_end(struct file *file,
unlock_page(page);
page_cache_release(page);

if (old_size < pos)
pagecache_isize_extended(inode, old_size, pos);

if (size_changed) {
ret2 = ext4_mark_inode_dirty(handle, inode);
if (!ret)
Expand Down
31 changes: 13 additions & 18 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2779,6 +2779,12 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
if (readonly)
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 */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT4_FEATURE_RO_COMPAT_SUPP)) {
ext4_msg(sb, KERN_ERR, "couldn't mount RDWR because of "
Expand Down Expand Up @@ -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));
spin_lock_init(&sbi->s_next_gen_lock);

init_timer(&sbi->s_err_report);
sbi->s_err_report.function = print_daily_error_info;
sbi->s_err_report.data = (unsigned long) sb;
setup_timer(&sbi->s_err_report, print_daily_error_info,
(unsigned long) sb);

/* Register extent status tree shrinker */
if (ext4_es_register_shrinker(sbi))
Expand Down Expand Up @@ -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)
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)) {
err = -EINVAL;
goto restore_opts;
Expand All @@ -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) ^
test_opt(sb, JOURNAL_CHECKSUM)) {
ext4_msg(sb, KERN_ERR, "changing journal_checksum "
"during remount not supported");
err = -EINVAL;
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;
"during remount not supported; ignoring");
sbi->s_mount_opt ^= EXT4_MOUNT_JOURNAL_CHECKSUM;
}

if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
Expand Down Expand Up @@ -4963,7 +4956,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
ext4_mark_recovery_complete(sb, es);
} else {
/* 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;
goto restore_opts;
}
Expand Down
3 changes: 3 additions & 0 deletions fs/jbd2/recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,9 @@ static int do_one_pass(journal_t *journal,
if (descr_csum_size > 0 &&
!jbd2_descr_block_csum_verify(journal,
bh->b_data)) {
printk(KERN_ERR "JBD2: Invalid checksum "
"recovering block %lu in log\n",
next_log_block);
err = -EIO;
brelse(bh);
goto failed;
Expand Down

0 comments on commit feaf222

Please sign in to comment.