Commit 4edebed8 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 updates from Theodore Ts'o:
 "The major new feature added in this update is Darrick J Wong's
  metadata checksum feature, which adds crc32 checksums to ext4's
  metadata fields.

  There is also the usual set of cleanups and bug fixes."

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (44 commits)
  ext4: hole-punch use truncate_pagecache_range
  jbd2: use kmem_cache_zalloc wrapper instead of flag
  ext4: remove mb_groups before tearing down the buddy_cache
  ext4: add ext4_mb_unload_buddy in the error path
  ext4: don't trash state flags in EXT4_IOC_SETFLAGS
  ext4: let getattr report the right blocks in delalloc+bigalloc
  ext4: add missing save_error_info() to ext4_error()
  ext4: add debugging trigger for ext4_error()
  ext4: protect group inode free counting with group lock
  ext4: use consistent ssize_t type in ext4_file_write()
  ext4: fix format flag in ext4_ext_binsearch_idx()
  ext4: cleanup in ext4_discard_allocated_blocks()
  ext4: return ENOMEM when mounts fail due to lack of memory
  ext4: remove redundundant "(char *) bh->b_data" casts
  ext4: disallow hard-linked directory in ext4_lookup
  ext4: fix potential integer overflow in alloc_flex_gd()
  ext4: remove needs_recovery in ext4_mb_init()
  ext4: force ro mount if ext4_setup_super() fails
  ext4: fix potential NULL dereference in ext4_free_inodes_counts()
  ext4/jbd2: add metadata checksumming to the list of supported features
  ...
parents 51eab603 5e44f8c3
...@@ -2,6 +2,8 @@ config EXT4_FS ...@@ -2,6 +2,8 @@ config EXT4_FS
tristate "The Extended 4 (ext4) filesystem" tristate "The Extended 4 (ext4) filesystem"
select JBD2 select JBD2
select CRC16 select CRC16
select CRYPTO
select CRYPTO_CRC32C
help help
This is the next generation of the ext3 filesystem. This is the next generation of the ext3 filesystem.
......
...@@ -168,12 +168,14 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ...@@ -168,12 +168,14 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *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(sbi, block_group, gdp)) { if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
ext4_error(sb, "Checksum bad for group %u", block_group); ext4_error(sb, "Checksum bad for group %u", block_group);
ext4_free_group_clusters_set(sb, gdp, 0); ext4_free_group_clusters_set(sb, gdp, 0);
ext4_free_inodes_set(sb, gdp, 0); ext4_free_inodes_set(sb, gdp, 0);
ext4_itable_unused_set(sb, gdp, 0); ext4_itable_unused_set(sb, gdp, 0);
memset(bh->b_data, 0xff, sb->s_blocksize); memset(bh->b_data, 0xff, sb->s_blocksize);
ext4_block_bitmap_csum_set(sb, block_group, gdp, bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
return; return;
} }
memset(bh->b_data, 0, sb->s_blocksize); memset(bh->b_data, 0, sb->s_blocksize);
...@@ -210,6 +212,9 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ...@@ -210,6 +212,9 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
*/ */
ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group), ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group),
sb->s_blocksize * 8, bh->b_data); sb->s_blocksize * 8, bh->b_data);
ext4_block_bitmap_csum_set(sb, block_group, gdp, bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, block_group, gdp);
} }
/* Return the number of free blocks in a block group. It is used when /* Return the number of free blocks in a block group. It is used when
...@@ -325,6 +330,23 @@ static int ext4_valid_block_bitmap(struct super_block *sb, ...@@ -325,6 +330,23 @@ static int ext4_valid_block_bitmap(struct super_block *sb,
block_group, bitmap_blk); block_group, bitmap_blk);
return 0; return 0;
} }
void ext4_validate_block_bitmap(struct super_block *sb,
struct ext4_group_desc *desc,
unsigned int block_group,
struct buffer_head *bh)
{
if (buffer_verified(bh))
return;
ext4_lock_group(sb, block_group);
if (ext4_valid_block_bitmap(sb, desc, block_group, bh) &&
ext4_block_bitmap_csum_verify(sb, block_group, desc, bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8))
set_buffer_verified(bh);
ext4_unlock_group(sb, block_group);
}
/** /**
* ext4_read_block_bitmap() * ext4_read_block_bitmap()
* @sb: super block * @sb: super block
...@@ -355,12 +377,12 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) ...@@ -355,12 +377,12 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
} }
if (bitmap_uptodate(bh)) if (bitmap_uptodate(bh))
return bh; goto verify;
lock_buffer(bh); lock_buffer(bh);
if (bitmap_uptodate(bh)) { if (bitmap_uptodate(bh)) {
unlock_buffer(bh); unlock_buffer(bh);
return bh; goto verify;
} }
ext4_lock_group(sb, block_group); ext4_lock_group(sb, block_group);
if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
...@@ -379,7 +401,7 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) ...@@ -379,7 +401,7 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
*/ */
set_bitmap_uptodate(bh); set_bitmap_uptodate(bh);
unlock_buffer(bh); unlock_buffer(bh);
return bh; goto verify;
} }
/* /*
* submit the buffer_head for reading * submit the buffer_head for reading
...@@ -390,6 +412,9 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) ...@@ -390,6 +412,9 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
get_bh(bh); get_bh(bh);
submit_bh(READ, bh); submit_bh(READ, bh);
return bh; return bh;
verify:
ext4_validate_block_bitmap(sb, desc, block_group, bh);
return bh;
} }
/* Returns 0 on success, 1 on error */ /* Returns 0 on success, 1 on error */
...@@ -412,7 +437,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group, ...@@ -412,7 +437,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
} }
clear_buffer_new(bh); clear_buffer_new(bh);
/* Panic or remount fs read-only if block bitmap is invalid */ /* Panic or remount fs read-only if block bitmap is invalid */
ext4_valid_block_bitmap(sb, desc, block_group, bh); ext4_validate_block_bitmap(sb, desc, block_group, bh);
return 0; return 0;
} }
......
...@@ -29,3 +29,86 @@ unsigned int ext4_count_free(struct buffer_head *map, unsigned int numchars) ...@@ -29,3 +29,86 @@ unsigned int ext4_count_free(struct buffer_head *map, unsigned int numchars)
#endif /* EXT4FS_DEBUG */ #endif /* EXT4FS_DEBUG */
int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
struct ext4_group_desc *gdp,
struct buffer_head *bh, int sz)
{
__u32 hi;
__u32 provided, calculated;
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return 1;
provided = le16_to_cpu(gdp->bg_inode_bitmap_csum_lo);
calculated = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) {
hi = le16_to_cpu(gdp->bg_inode_bitmap_csum_hi);
provided |= (hi << 16);
} else
calculated &= 0xFFFF;
return provided == calculated;
}
void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
struct ext4_group_desc *gdp,
struct buffer_head *bh, int sz)
{
__u32 csum;
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return;
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
gdp->bg_inode_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
gdp->bg_inode_bitmap_csum_hi = cpu_to_le16(csum >> 16);
}
int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
struct ext4_group_desc *gdp,
struct buffer_head *bh, int sz)
{
__u32 hi;
__u32 provided, calculated;
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return 1;
provided = le16_to_cpu(gdp->bg_block_bitmap_csum_lo);
calculated = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END) {
hi = le16_to_cpu(gdp->bg_block_bitmap_csum_hi);
provided |= (hi << 16);
} else
calculated &= 0xFFFF;
if (provided == calculated)
return 1;
ext4_error(sb, "Bad block bitmap checksum: block_group = %u", group);
return 0;
}
void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
struct ext4_group_desc *gdp,
struct buffer_head *bh, int sz)
{
__u32 csum;
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return;
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
gdp->bg_block_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
gdp->bg_block_bitmap_csum_hi = cpu_to_le16(csum >> 16);
}
...@@ -179,6 +179,18 @@ static int ext4_readdir(struct file *filp, ...@@ -179,6 +179,18 @@ static int ext4_readdir(struct file *filp,
continue; continue;
} }
/* Check the checksum */
if (!buffer_verified(bh) &&
!ext4_dirent_csum_verify(inode,
(struct ext4_dir_entry *)bh->b_data)) {
EXT4_ERROR_FILE(filp, 0, "directory fails checksum "
"at offset %llu",
(unsigned long long)filp->f_pos);
filp->f_pos += sb->s_blocksize - offset;
continue;
}
set_buffer_verified(bh);
revalidate: revalidate:
/* If the dir block has changed since the last call to /* If the dir block has changed since the last call to
* readdir(2), then we might be pointing to an invalid * readdir(2), then we might be pointing to an invalid
......
This diff is collapsed.
...@@ -63,8 +63,21 @@ ...@@ -63,8 +63,21 @@
* ext4_inode has i_block array (60 bytes total). * ext4_inode has i_block array (60 bytes total).
* The first 12 bytes store ext4_extent_header; * The first 12 bytes store ext4_extent_header;
* the remainder stores an array of ext4_extent. * the remainder stores an array of ext4_extent.
* For non-inode extent blocks, ext4_extent_tail
* follows the array.
*/ */
/*
* This is the extent tail on-disk structure.
* All other extent structures are 12 bytes long. It turns out that
* block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
* covers all valid ext4 block sizes. Therefore, this tail structure can be
* crammed into the end of the block without having to rebalance the tree.
*/
struct ext4_extent_tail {
__le32 et_checksum; /* crc32c(uuid+inum+extent_block) */
};
/* /*
* This is the extent on-disk structure. * This is the extent on-disk structure.
* It's used at the bottom of the tree. * It's used at the bottom of the tree.
...@@ -101,6 +114,17 @@ struct ext4_extent_header { ...@@ -101,6 +114,17 @@ struct ext4_extent_header {
#define EXT4_EXT_MAGIC cpu_to_le16(0xf30a) #define EXT4_EXT_MAGIC cpu_to_le16(0xf30a)
#define EXT4_EXTENT_TAIL_OFFSET(hdr) \
(sizeof(struct ext4_extent_header) + \
(sizeof(struct ext4_extent) * le16_to_cpu((hdr)->eh_max)))
static inline struct ext4_extent_tail *
find_ext4_extent_tail(struct ext4_extent_header *eh)
{
return (struct ext4_extent_tail *)(((void *)eh) +
EXT4_EXTENT_TAIL_OFFSET(eh));
}
/* /*
* Array of ext4_ext_path contains path to some extent. * Array of ext4_ext_path contains path to some extent.
* Creation/lookup routines use it for traversal/splitting/etc. * Creation/lookup routines use it for traversal/splitting/etc.
......
...@@ -138,16 +138,23 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, ...@@ -138,16 +138,23 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
} }
int __ext4_handle_dirty_super(const char *where, unsigned int line, int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb) handle_t *handle, struct super_block *sb,
int now)
{ {
struct buffer_head *bh = EXT4_SB(sb)->s_sbh; struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
int err = 0; int err = 0;
if (ext4_handle_valid(handle)) { if (ext4_handle_valid(handle)) {
ext4_superblock_csum_set(sb,
(struct ext4_super_block *)bh->b_data);
err = jbd2_journal_dirty_metadata(handle, bh); err = jbd2_journal_dirty_metadata(handle, bh);
if (err) if (err)
ext4_journal_abort_handle(where, line, __func__, ext4_journal_abort_handle(where, line, __func__,
bh, handle, err); bh, handle, err);
} else if (now) {
ext4_superblock_csum_set(sb,
(struct ext4_super_block *)bh->b_data);
mark_buffer_dirty(bh);
} else } else
sb->s_dirt = 1; sb->s_dirt = 1;
return err; return err;
......
...@@ -213,7 +213,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, ...@@ -213,7 +213,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
struct buffer_head *bh); struct buffer_head *bh);
int __ext4_handle_dirty_super(const char *where, unsigned int line, int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb); handle_t *handle, struct super_block *sb,
int now);
#define ext4_journal_get_write_access(handle, bh) \ #define ext4_journal_get_write_access(handle, bh) \
__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh)) __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
...@@ -225,8 +226,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, ...@@ -225,8 +226,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
#define ext4_handle_dirty_metadata(handle, inode, bh) \ #define ext4_handle_dirty_metadata(handle, inode, bh) \
__ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \ __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
(bh)) (bh))
#define ext4_handle_dirty_super_now(handle, sb) \
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 1)
#define ext4_handle_dirty_super(handle, sb) \ #define ext4_handle_dirty_super(handle, sb) \
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb)) __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 0)
handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle); int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
......
...@@ -52,6 +52,46 @@ ...@@ -52,6 +52,46 @@
#define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */ #define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */
#define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */ #define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */
static __le32 ext4_extent_block_csum(struct inode *inode,
struct ext4_extent_header *eh)
{
struct ext4_inode_info *ei = EXT4_I(inode);
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
__u32 csum;
csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)eh,
EXT4_EXTENT_TAIL_OFFSET(eh));
return cpu_to_le32(csum);
}
static int ext4_extent_block_csum_verify(struct inode *inode,
struct ext4_extent_header *eh)
{
struct ext4_extent_tail *et;
if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return 1;
et = find_ext4_extent_tail(eh);
if (et->et_checksum != ext4_extent_block_csum(inode, eh))
return 0;
return 1;
}
static void ext4_extent_block_csum_set(struct inode *inode,
struct ext4_extent_header *eh)
{
struct ext4_extent_tail *et;
if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return;
et = find_ext4_extent_tail(eh);
et->et_checksum = ext4_extent_block_csum(inode, eh);
}
static int ext4_split_extent(handle_t *handle, static int ext4_split_extent(handle_t *handle,
struct inode *inode, struct inode *inode,
struct ext4_ext_path *path, struct ext4_ext_path *path,
...@@ -117,6 +157,7 @@ static int __ext4_ext_dirty(const char *where, unsigned int line, ...@@ -117,6 +157,7 @@ static int __ext4_ext_dirty(const char *where, unsigned int line,
{ {
int err; int err;
if (path->p_bh) { if (path->p_bh) {
ext4_extent_block_csum_set(inode, ext_block_hdr(path->p_bh));
/* path points to block */ /* path points to block */
err = __ext4_handle_dirty_metadata(where, line, handle, err = __ext4_handle_dirty_metadata(where, line, handle,
inode, path->p_bh); inode, path->p_bh);
...@@ -391,6 +432,12 @@ static int __ext4_ext_check(const char *function, unsigned int line, ...@@ -391,6 +432,12 @@ static int __ext4_ext_check(const char *function, unsigned int line,
error_msg = "invalid extent entries"; error_msg = "invalid extent entries";
goto corrupted; goto corrupted;
} }
/* Verify checksum on non-root extent tree nodes */
if (ext_depth(inode) != depth &&
!ext4_extent_block_csum_verify(inode, eh)) {
error_msg = "extent tree corrupted";
goto corrupted;
}
return 0; return 0;
corrupted: corrupted:
...@@ -412,6 +459,26 @@ int ext4_ext_check_inode(struct inode *inode) ...@@ -412,6 +459,26 @@ int ext4_ext_check_inode(struct inode *inode)
return ext4_ext_check(inode, ext_inode_hdr(inode), ext_depth(inode)); return ext4_ext_check(inode, ext_inode_hdr(inode), ext_depth(inode));
} }
static int __ext4_ext_check_block(const char *function, unsigned int line,
struct inode *inode,
struct ext4_extent_header *eh,
int depth,
struct buffer_head *bh)
{
int ret;
if (buffer_verified(bh))
return 0;
ret = ext4_ext_check(inode, eh, depth);
if (ret)
return ret;
set_buffer_verified(bh);
return ret;
}
#define ext4_ext_check_block(inode, eh, depth, bh) \
__ext4_ext_check_block(__func__, __LINE__, inode, eh, depth, bh)
#ifdef EXT_DEBUG #ifdef EXT_DEBUG
static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
{ {
...@@ -536,7 +603,7 @@ ext4_ext_binsearch_idx(struct inode *inode, ...@@ -536,7 +603,7 @@ ext4_ext_binsearch_idx(struct inode *inode,
} }
path->p_idx = l - 1; path->p_idx = l - 1;
ext_debug(" -> %d->%lld ", le32_to_cpu(path->p_idx->ei_block), ext_debug(" -> %u->%lld ", le32_to_cpu(path->p_idx->ei_block),
ext4_idx_pblock(path->p_idx)); ext4_idx_pblock(path->p_idx));
#ifdef CHECK_BINSEARCH #ifdef CHECK_BINSEARCH
...@@ -668,8 +735,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, ...@@ -668,8 +735,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
i = depth; i = depth;
/* walk through the tree */ /* walk through the tree */
while (i) { while (i) {
int need_to_validate = 0;
ext_debug("depth %d: num %d, max %d\n", ext_debug("depth %d: num %d, max %d\n",
ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max)); ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
...@@ -688,8 +753,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, ...@@ -688,8 +753,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
put_bh(bh); put_bh(bh);
goto err; goto err;
} }
/* validate the extent entries */
need_to_validate = 1;
} }
eh = ext_block_hdr(bh); eh = ext_block_hdr(bh);
ppos++; ppos++;
...@@ -703,7 +766,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, ...@@ -703,7 +766,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
path[ppos].p_hdr = eh; path[ppos].p_hdr = eh;
i--; i--;
if (need_to_validate && ext4_ext_check(inode, eh, i)) if (ext4_ext_check_block(inode, eh, i, bh))
goto err; goto err;
} }
...@@ -914,6 +977,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, ...@@ -914,6 +977,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
le16_add_cpu(&neh->eh_entries, m); le16_add_cpu(&neh->eh_entries, m);
} }
ext4_extent_block_csum_set(inode, neh);
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
unlock_buffer(bh); unlock_buffer(bh);
...@@ -992,6 +1056,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, ...@@ -992,6 +1056,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
sizeof(struct ext4_extent_idx) * m); sizeof(struct ext4_extent_idx) * m);
le16_add_cpu(&neh->eh_entries, m); le16_add_cpu(&neh->eh_entries, m);
} }
ext4_extent_block_csum_set(inode, neh);
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
unlock_buffer(bh); unlock_buffer(bh);
...@@ -1089,6 +1154,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, ...@@ -1089,6 +1154,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
else else
neh->eh_max = cpu_to_le16(ext4_ext_space_block(inode, 0)); neh->eh_max = cpu_to_le16(ext4_ext_space_block(inode, 0));
neh->eh_magic = EXT4_EXT_MAGIC; neh->eh_magic = EXT4_EXT_MAGIC;
ext4_extent_block_csum_set(inode, neh);
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
unlock_buffer(bh); unlock_buffer(bh);
...@@ -1344,7 +1410,8 @@ static int ext4_ext_search_right(struct inode *inode, ...@@ -1344,7 +1410,8 @@ static int ext4_ext_search_right(struct inode *inode,
return -EIO; return -EIO;
eh = ext_block_hdr(bh); eh = ext_block_hdr(bh);
/* subtract from p_depth to get proper eh_depth */ /* subtract from p_depth to get proper eh_depth */
if (ext4_ext_check(inode, eh, path->p_depth - depth)) { if (ext4_ext_check_block(inode, eh,
path->p_depth - depth, bh)) {
put_bh(bh); put_bh(bh);
return -EIO; return -EIO;
} }
...@@ -1357,7 +1424,7 @@ static int ext4_ext_search_right(struct inode *inode, ...@@ -1357,7 +1424,7 @@ static int ext4_ext_search_right(struct inode *inode,
if (bh == NULL) if (bh == NULL)
return -EIO; return -EIO;
eh = ext_block_hdr(bh); eh = ext_block_hdr(bh);
if (ext4_ext_check(inode, eh, path->p_depth - depth)) { if (ext4_ext_check_block(inode, eh, path->p_depth - depth, bh)) {
put_bh(bh); put_bh(bh);
return -EIO; return -EIO;
} }
...@@ -2644,8 +2711,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2644,8 +2711,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
err = -EIO; err = -EIO;
break; break;
} }
if (ext4_ext_check(inode, ext_block_hdr(bh), if (ext4_ext_check_block(inode, ext_block_hdr(bh),
depth - i - 1)) { depth - i - 1, bh)) {
err = -EIO; err = -EIO;
break; break;
} }
...@@ -4722,8 +4789,8 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) ...@@ -4722,8 +4789,8 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
/* Now release the pages */ /* Now release the pages */
if (last_page_offset > first_page_offset) { if (last_page_offset > first_page_offset) {
truncate_inode_pages_range(mapping, first_page_offset, truncate_pagecache_range(inode, first_page_offset,
last_page_offset-1); last_page_offset - 1);
} }
/* finish any pending end_io work */ /* finish any pending end_io work */
......
...@@ -95,7 +95,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -95,7 +95,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
{ {
struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
int unaligned_aio = 0; int unaligned_aio = 0;
int ret; ssize_t ret;
/* /*
* If we have encountered a bitmap-format file, the size limit * If we have encountered a bitmap-format file, the size limit
......
...@@ -70,24 +70,27 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb, ...@@ -70,24 +70,27 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,
ext4_group_t block_group, ext4_group_t block_group,
struct ext4_group_desc *gdp) struct ext4_group_desc *gdp)
{ {
struct ext4_sb_info *sbi = EXT4_SB(sb);
J_ASSERT_BH(bh, buffer_locked(bh)); J_ASSERT_BH(bh, buffer_locked(bh));
/* If checksum is bad mark all blocks and inodes use to prevent /* If checksum is bad mark all blocks and inodes use to prevent
* allocation, essentially implementing a per-group read-only flag. */ * allocation, essentially implementing a per-group read-only flag. */
if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
ext4_error(sb, "Checksum bad for group %u", block_group); ext4_error(sb, "Checksum bad for group %u", block_group);
ext4_free_group_clusters_set(sb, gdp, 0); ext4_free_group_clusters_set(sb, gdp, 0);
ext4_free_inodes_set(sb, gdp, 0); ext4_free_inodes_set(sb, gdp, 0);
ext4_itable_unused_set(sb, gdp, 0); ext4_itable_unused_set(sb, gdp, 0);
memset(bh->b_data, 0xff, sb->s_blocksize); memset(bh->b_data, 0xff, sb->s_blocksize);
ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh,
EXT4_INODES_PER_GROUP(sb) / 8);
return 0; return 0;
} }
memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);
ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8, ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8,
bh->b_data); bh->b_data);
ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh,
EXT4_INODES_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, block_group, gdp);
return EXT4_INODES_PER_GROUP(sb); return EXT4_INODES_PER_GROUP(sb);
} }
...@@ -128,12 +131,12 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) ...@@ -128,12 +131,12 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
return NULL; return NULL;
} }
if (bitmap_uptodate(bh)) if (bitmap_uptodate(bh))
return bh; goto verify;
lock_buffer(bh); lock_buffer(bh);
if (bitmap_uptodate(bh)) { if (bitmap_uptodate(bh)) {
unlock_buffer(bh); unlock_buffer(bh);
return bh; goto verify;
} }
ext4_lock_group(sb, block_group); ext4_lock_group(sb, block_group);
...@@ -141,6 +144,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) ...@@ -141,6 +144,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
ext4_init_inode_bitmap(sb, bh, block_group, desc); ext4_init_inode_bitmap(sb, bh, block_group, desc);
set_bitmap_uptodate(bh); set_bitmap_uptodate(bh);
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
set_buffer_verified(bh);
ext4_unlock_group(sb, block_group); ext4_unlock_group(sb, block_group);
unlock_buffer(bh); unlock_buffer(bh);
return bh; return bh;
...@@ -154,7 +158,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) ...@@ -154,7 +158,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
*/ */
set_bitmap_uptodate(bh); set_bitmap_uptodate(bh);
unlock_buffer(bh); unlock_buffer(bh);
return bh; goto verify;
} }
/* /*
* submit the buffer_head for reading * submit the buffer_head for reading
...@@ -171,6 +175,20 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) ...@@ -171,6 +175,20 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
block_group, bitmap_blk); block_group, bitmap_blk);
return NULL; return NULL;
} }
verify:
ext4_lock_group(sb, block_group);
if (!buffer_verified(bh) &&
!ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh,
EXT4_INODES_PER_GROUP(sb) / 8)) {
ext4_unlock_group(sb, block_group);
put_bh(bh);
ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
"inode_bitmap = %llu", block_group, bitmap_blk);
return NULL;
}
ext4_unlock_group(sb, block_group);
set_buffer_verified(bh);
return bh; return bh;
} }
...@@ -276,7 +294,9 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) ...@@ -276,7 +294,9 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
ext4_used_dirs_set(sb, gdp, count); ext4_used_dirs_set(sb, gdp, count);
percpu_counter_dec(&sbi->s_dirs_counter); percpu_counter_dec(&sbi->s_dirs_counter);
} }
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp); ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
EXT4_INODES_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, block_group, gdp);
ext4_unlock_group(sb, block_group); ext4_unlock_group(sb, block_group);
percpu_counter_inc(&sbi->s_freeinodes_counter); percpu_counter_inc(&sbi->s_freeinodes_counter);
...@@ -488,12 +508,14 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, ...@@ -488,12 +508,14 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent,
for (i = 0; i < ngroups; i++) { for (i = 0; i < ngroups; i++) {
grp = (parent_group + i) % ngroups; grp = (parent_group + i) % ngroups;
desc = ext4_get_group_desc(sb, grp, NULL); desc = ext4_get_group_desc(sb, grp, NULL);
if (desc) {
grp_free = ext4_free_inodes_count(sb, desc); grp_free = ext4_free_inodes_count(sb, desc);
if (desc && grp_free && grp_free >= avefreei) { if (grp_free && grp_free >= avefreei) {
*group = grp; *group = grp;
return 0; return 0;
} }
} }
}
if (avefreei) { if (avefreei) {
/* /*
...@@ -709,7 +731,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -709,7 +731,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
got: got:
/* We may have to initialize the block bitmap if it isn't already */ /* We may have to initialize the block bitmap if it isn't already */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && if (ext4_has_group_desc_csum(sb) &&
gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
struct buffer_head *block_bitmap_bh; struct buffer_head *block_bitmap_bh;
...@@ -731,8 +753,11 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -731,8 +753,11 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
ext4_free_group_clusters_set(sb, gdp, ext4_free_group_clusters_set(sb, gdp,
ext4_free_clusters_after_init(sb, group, gdp)); ext4_free_clusters_after_init(sb, group, gdp));
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, ext4_block_bitmap_csum_set(sb, group, gdp,
gdp); block_bitmap_bh,
EXT4_BLOCKS_PER_GROUP(sb) /
8);
ext4_group_desc_csum_set(sb, group, gdp);
} }
ext4_unlock_group(sb, group); ext4_unlock_group(sb, group);
...@@ -751,7 +776,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -751,7 +776,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
goto fail; goto fail;
/* Update the relevant bg descriptor fields */ /* Update the relevant bg descriptor fields */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { if (ext4_has_group_desc_csum(sb)) {
int free; int free;
struct ext4_group_info *grp = ext4_get_group_info(sb, group); struct ext4_group_info *grp = ext4_get_group_info(sb, group);
...@@ -772,7 +797,10 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -772,7 +797,10 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
ext4_itable_unused_set(sb, gdp, ext4_itable_unused_set(sb, gdp,
(EXT4_INODES_PER_GROUP(sb) - ino)); (EXT4_INODES_PER_GROUP(sb) - ino));
up_read(&grp->alloc_sem); up_read(&grp->alloc_sem);
} else {
ext4_lock_group(sb, group);
} }
ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1); ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
if (S_ISDIR(mode)) { if (S_ISDIR(mode)) {
ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1); ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
...@@ -782,10 +810,12 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -782,10 +810,12 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
atomic_inc(&sbi->s_flex_groups[f].used_dirs); atomic_inc(&sbi->s_flex_groups[f].used_dirs);
} }
} }
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { if (ext4_has_group_desc_csum(sb)) {
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); ext4_inode_bitmap_csum_set(sb, group, gdp, inode_bitmap_bh,
ext4_unlock_group(sb, group); EXT4_INODES_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, group, gdp);
} }
ext4_unlock_group(sb, group);
BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata"); BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh); err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh);
...@@ -850,6 +880,19 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -850,6 +880,19 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
inode->i_generation = sbi->s_next_generation++; inode->i_generation = sbi->s_next_generation++;
spin_unlock(&sbi->s_next_gen_lock); spin_unlock(&sbi->s_next_gen_lock);
/* Precompute checksum seed for inode metadata */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
__u32 csum;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
__le32 inum = cpu_to_le32(inode->i_ino);
__le32 gen = cpu_to_le32(inode->i_generation);
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum,
sizeof(inum));
ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen,
sizeof(gen));
}
ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
ext4_set_inode_state(inode, EXT4_STATE_NEW); ext4_set_inode_state(inode, EXT4_STATE_NEW);
...@@ -1140,7 +1183,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group, ...@@ -1140,7 +1183,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group,
skip_zeroout: skip_zeroout:
ext4_lock_group(sb, group); ext4_lock_group(sb, group);
gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_ZEROED); gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_ZEROED);
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); ext4_group_desc_csum_set(sb, group, gdp);
ext4_unlock_group(sb, group); ext4_unlock_group(sb, group);
BUFFER_TRACE(group_desc_bh, BUFFER_TRACE(group_desc_bh,
......
...@@ -47,6 +47,73 @@ ...@@ -47,6 +47,73 @@
#define MPAGE_DA_EXTENT_TAIL 0x01 #define MPAGE_DA_EXTENT_TAIL 0x01
static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw,
struct ext4_inode_info *ei)
{
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
__u16 csum_lo;
__u16 csum_hi = 0;
__u32 csum;
csum_lo = raw->i_checksum_lo;
raw->i_checksum_lo = 0;
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
csum_hi = raw->i_checksum_hi;
raw->i_checksum_hi = 0;
}
csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw,
EXT4_INODE_SIZE(inode->i_sb));
raw->i_checksum_lo = csum_lo;
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
raw->i_checksum_hi = csum_hi;
return csum;
}
static int ext4_inode_csum_verify(struct inode *inode, struct ext4_inode *raw,
struct ext4_inode_info *ei)
{
__u32 provided, calculated;
if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
cpu_to_le32(EXT4_OS_LINUX) ||
!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return 1;
provided = le16_to_cpu(raw->i_checksum_lo);
calculated = ext4_inode_csum(inode, raw, ei);
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
provided |= ((__u32)le16_to_cpu(raw->i_checksum_hi)) << 16;
else
calculated &= 0xFFFF;
return provided == calculated;
}
static void ext4_inode_csum_set(struct inode *inode, struct ext4_inode *raw,
struct ext4_inode_info *ei)
{
__u32 csum;
if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
cpu_to_le32(EXT4_OS_LINUX) ||
!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return;
csum = ext4_inode_csum(inode, raw, ei);
raw->i_checksum_lo = cpu_to_le16(csum & 0xFFFF);
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
raw->i_checksum_hi = cpu_to_le16(csum >> 16);
}
static inline int ext4_begin_ordered_truncate(struct inode *inode, static inline int ext4_begin_ordered_truncate(struct inode *inode,
loff_t new_size) loff_t new_size)
{ {
...@@ -3517,8 +3584,7 @@ static int __ext4_get_inode_loc(struct inode *inode, ...@@ -3517,8 +3584,7 @@ static int __ext4_get_inode_loc(struct inode *inode,
b = table; b = table;
end = b + EXT4_SB(sb)->s_inode_readahead_blks; end = b + EXT4_SB(sb)->s_inode_readahead_blks;
num = EXT4_INODES_PER_GROUP(sb); num = EXT4_INODES_PER_GROUP(sb);
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, if (ext4_has_group_desc_csum(sb))
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
num -= ext4_itable_unused_count(sb, gdp); num -= ext4_itable_unused_count(sb, gdp);
table += num / inodes_per_block; table += num / inodes_per_block;
if (end > table) if (end > table)
...@@ -3646,6 +3712,39 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ...@@ -3646,6 +3712,39 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
if (ret < 0) if (ret < 0)
goto bad_inode; goto bad_inode;
raw_inode = ext4_raw_inode(&iloc); raw_inode = ext4_raw_inode(&iloc);
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
EXT4_INODE_SIZE(inode->i_sb)) {
EXT4_ERROR_INODE(inode, "bad extra_isize (%u != %u)",
EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize,
EXT4_INODE_SIZE(inode->i_sb));
ret = -EIO;
goto bad_inode;
}
} else
ei->i_extra_isize = 0;
/* Precompute checksum seed for inode metadata */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
__u32 csum;
__le32 inum = cpu_to_le32(inode->i_ino);
__le32 gen = raw_inode->i_generation;
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum,
sizeof(inum));
ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen,
sizeof(gen));
}
if (!ext4_inode_csum_verify(inode, raw_inode, ei)) {
EXT4_ERROR_INODE(inode, "checksum invalid");
ret = -EIO;
goto bad_inode;
}
inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_mode = le16_to_cpu(raw_inode->i_mode);
i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
...@@ -3725,12 +3824,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ...@@ -3725,12 +3824,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
} }
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) { if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
EXT4_INODE_SIZE(inode->i_sb)) {
ret = -EIO;
goto bad_inode;
}
if (ei->i_extra_isize == 0) { if (ei->i_extra_isize == 0) {
/* The extra space is currently unused. Use it. */ /* The extra space is currently unused. Use it. */
ei->i_extra_isize = sizeof(struct ext4_inode) - ei->i_extra_isize = sizeof(struct ext4_inode) -
...@@ -3742,8 +3835,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ...@@ -3742,8 +3835,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
ext4_set_inode_state(inode, EXT4_STATE_XATTR); ext4_set_inode_state(inode, EXT4_STATE_XATTR);
} }
} else }
ei->i_extra_isize = 0;
EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode); EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode);
EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode); EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode);
...@@ -3942,7 +4034,7 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -3942,7 +4034,7 @@ static int ext4_do_update_inode(handle_t *handle,
EXT4_SET_RO_COMPAT_FEATURE(sb, EXT4_SET_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE); EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
ext4_handle_sync(handle); ext4_handle_sync(handle);
err = ext4_handle_dirty_super(handle, sb); err = ext4_handle_dirty_super_now(handle, sb);
} }
} }
raw_inode->i_generation = cpu_to_le32(inode->i_generation); raw_inode->i_generation = cpu_to_le32(inode->i_generation);
...@@ -3969,6 +4061,8 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -3969,6 +4061,8 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize); raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
} }
ext4_inode_csum_set(inode, raw_inode, ei);
BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
rc = ext4_handle_dirty_metadata(handle, NULL, bh); rc = ext4_handle_dirty_metadata(handle, NULL, bh);
if (!err) if (!err)
...@@ -4213,7 +4307,8 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -4213,7 +4307,8 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
* will return the blocks that include the delayed allocation * will return the blocks that include the delayed allocation
* blocks for this file. * blocks for this file.
*/ */
delalloc_blocks = EXT4_I(inode)->i_reserved_data_blocks; delalloc_blocks = EXT4_C2B(EXT4_SB(inode->i_sb),
EXT4_I(inode)->i_reserved_data_blocks);
stat->blocks += (delalloc_blocks << inode->i_sb->s_blocksize_bits)>>9; stat->blocks += (delalloc_blocks << inode->i_sb->s_blocksize_bits)>>9;
return 0; return 0;
......
...@@ -38,7 +38,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -38,7 +38,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
handle_t *handle = NULL; handle_t *handle = NULL;
int err, migrate = 0; int err, migrate = 0;
struct ext4_iloc iloc; struct ext4_iloc iloc;
unsigned int oldflags; unsigned int oldflags, mask, i;
unsigned int jflag; unsigned int jflag;
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
...@@ -115,8 +115,14 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -115,8 +115,14 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (err) if (err)
goto flags_err; goto flags_err;
flags = flags & EXT4_FL_USER_MODIFIABLE; for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE; if (!(mask & EXT4_FL_USER_MODIFIABLE))
continue;
if (mask & flags)
ext4_set_inode_flag(inode, i);
else
ext4_clear_inode_flag(inode, i);
}
ei->i_flags = flags; ei->i_flags = flags;
ext4_set_inode_flags(inode); ext4_set_inode_flags(inode);
...@@ -152,6 +158,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -152,6 +158,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
return -EPERM; return -EPERM;
if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
ext4_warning(sb, "Setting inode version is not "
"supported with metadata_csum enabled.");
return -ENOTTY;
}
err = mnt_want_write_file(filp); err = mnt_want_write_file(filp);
if (err) if (err)
return err; return err;
......
...@@ -788,7 +788,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore) ...@@ -788,7 +788,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
int first_block; int first_block;
struct super_block *sb; struct super_block *sb;
struct buffer_head *bhs; struct buffer_head *bhs;
struct buffer_head **bh; struct buffer_head **bh = NULL;
struct inode *inode; struct inode *inode;
char *data; char *data;
char *bitmap; char *bitmap;
...@@ -2375,7 +2375,7 @@ static int ext4_groupinfo_create_slab(size_t size) ...@@ -2375,7 +2375,7 @@ static int ext4_groupinfo_create_slab(size_t size)
return 0; return 0;
} }
int ext4_mb_init(struct super_block *sb, int needs_recovery) int ext4_mb_init(struct super_block *sb)
{ {
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
unsigned i, j; unsigned i, j;
...@@ -2517,6 +2517,9 @@ int ext4_mb_release(struct super_block *sb) ...@@ -2517,6 +2517,9 @@ int ext4_mb_release(struct super_block *sb)
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits);
if (sbi->s_proc)
remove_proc_entry("mb_groups", sbi->s_proc);
if (sbi->s_group_info) { if (sbi->s_group_info) {
for (i = 0; i < ngroups; i++) { for (i = 0; i < ngroups; i++) {
grinfo = ext4_get_group_info(sb, i); grinfo = ext4_get_group_info(sb, i);
...@@ -2564,8 +2567,6 @@ int ext4_mb_release(struct super_block *sb) ...@@ -2564,8 +2567,6 @@ int ext4_mb_release(struct super_block *sb)
} }
free_percpu(sbi->s_locality_groups); free_percpu(sbi->s_locality_groups);
if (sbi->s_proc)
remove_proc_entry("mb_groups", sbi->s_proc);
return 0; return 0;
} }
...@@ -2797,7 +2798,9 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ...@@ -2797,7 +2798,9 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
} }
len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len; len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len;
ext4_free_group_clusters_set(sb, gdp, len); ext4_free_group_clusters_set(sb, gdp, len);
gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp); ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, ac->ac_b_ex.fe_group, gdp);
ext4_unlock_group(sb, ac->ac_b_ex.fe_group); ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
percpu_counter_sub(&sbi->s_freeclusters_counter, ac->ac_b_ex.fe_len); percpu_counter_sub(&sbi->s_freeclusters_counter, ac->ac_b_ex.fe_len);
...@@ -3071,13 +3074,9 @@ static void ext4_mb_collect_stats(struct ext4_allocation_context *ac) ...@@ -3071,13 +3074,9 @@ static void ext4_mb_collect_stats(struct ext4_allocation_context *ac)
static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac) static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac)
{ {
struct ext4_prealloc_space *pa = ac->ac_pa; struct ext4_prealloc_space *pa = ac->ac_pa;
int len;
if (pa && pa->pa_type == MB_INODE_PA) {
len = ac->ac_b_ex.fe_len;
pa->pa_free += len;
}
if (pa && pa->pa_type == MB_INODE_PA)
pa->pa_free += ac->ac_b_ex.fe_len;
} }
/* /*
...@@ -4636,6 +4635,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4636,6 +4635,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
*/ */
new_entry = kmem_cache_alloc(ext4_free_data_cachep, GFP_NOFS); new_entry = kmem_cache_alloc(ext4_free_data_cachep, GFP_NOFS);
if (!new_entry) { if (!new_entry) {
ext4_mb_unload_buddy(&e4b);
err = -ENOMEM; err = -ENOMEM;
goto error_return; goto error_return;
} }
...@@ -4659,7 +4659,9 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4659,7 +4659,9 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
ret = ext4_free_group_clusters(sb, gdp) + count_clusters; ret = ext4_free_group_clusters(sb, gdp) + count_clusters;
ext4_free_group_clusters_set(sb, gdp, ret); ext4_free_group_clusters_set(sb, gdp, ret);
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp); ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, block_group, gdp);
ext4_unlock_group(sb, block_group); ext4_unlock_group(sb, block_group);
percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters); percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters);
...@@ -4803,7 +4805,9 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, ...@@ -4803,7 +4805,9 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
mb_free_blocks(NULL, &e4b, bit, count); mb_free_blocks(NULL, &e4b, bit, count);
blk_free_count = blocks_freed + ext4_free_group_clusters(sb, desc); blk_free_count = blocks_freed + ext4_free_group_clusters(sb, desc);
ext4_free_group_clusters_set(sb, desc, blk_free_count); ext4_free_group_clusters_set(sb, desc, blk_free_count);
desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc); ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
ext4_group_desc_csum_set(sb, block_group, desc);
ext4_unlock_group(sb, block_group); ext4_unlock_group(sb, block_group);
percpu_counter_add(&sbi->s_freeclusters_counter, percpu_counter_add(&sbi->s_freeclusters_counter,
EXT4_B2C(sbi, blocks_freed)); EXT4_B2C(sbi, blocks_freed));
......
...@@ -6,12 +6,45 @@ ...@@ -6,12 +6,45 @@
#include "ext4.h" #include "ext4.h"
/* Checksumming functions */
static __u32 ext4_mmp_csum(struct super_block *sb, struct mmp_struct *mmp)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
int offset = offsetof(struct mmp_struct, mmp_checksum);
__u32 csum;
csum = ext4_chksum(sbi, sbi->s_csum_seed, (char *)mmp, offset);
return cpu_to_le32(csum);
}
int ext4_mmp_csum_verify(struct super_block *sb, struct mmp_struct *mmp)
{
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return 1;
return mmp->mmp_checksum == ext4_mmp_csum(sb, mmp);
}
void ext4_mmp_csum_set(struct super_block *sb, struct mmp_struct *mmp)
{
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return;
mmp->mmp_checksum = ext4_mmp_csum(sb, mmp);
}
/* /*
* Write the MMP block using WRITE_SYNC to try to get the block on-disk * Write the MMP block using WRITE_SYNC to try to get the block on-disk
* faster. * faster.
*/ */
static int write_mmp_block(struct buffer_head *bh) static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
{ {
struct mmp_struct *mmp = (struct mmp_struct *)(bh->b_data);
ext4_mmp_csum_set(sb, mmp);
mark_buffer_dirty(bh); 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;
...@@ -59,7 +92,8 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh, ...@@ -59,7 +92,8 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh,
} }
mmp = (struct mmp_struct *)((*bh)->b_data); mmp = (struct mmp_struct *)((*bh)->b_data);
if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC ||
!ext4_mmp_csum_verify(sb, mmp))
return -EINVAL; return -EINVAL;
return 0; return 0;
...@@ -120,7 +154,7 @@ static int kmmpd(void *data) ...@@ -120,7 +154,7 @@ static int kmmpd(void *data)
mmp->mmp_time = cpu_to_le64(get_seconds()); mmp->mmp_time = cpu_to_le64(get_seconds());
last_update_time = jiffies; last_update_time = jiffies;
retval = write_mmp_block(bh); retval = write_mmp_block(sb, bh);
/* /*
* Don't spew too many error messages. Print one every * Don't spew too many error messages. Print one every
* (s_mmp_update_interval * 60) seconds. * (s_mmp_update_interval * 60) seconds.
...@@ -200,7 +234,7 @@ static int kmmpd(void *data) ...@@ -200,7 +234,7 @@ static int kmmpd(void *data)
mmp->mmp_seq = cpu_to_le32(EXT4_MMP_SEQ_CLEAN); mmp->mmp_seq = cpu_to_le32(EXT4_MMP_SEQ_CLEAN);
mmp->mmp_time = cpu_to_le64(get_seconds()); mmp->mmp_time = cpu_to_le64(get_seconds());
retval = write_mmp_block(bh); retval = write_mmp_block(sb, bh);
failed: failed:
kfree(data); kfree(data);
...@@ -299,7 +333,7 @@ int ext4_multi_mount_protect(struct super_block *sb, ...@@ -299,7 +333,7 @@ int ext4_multi_mount_protect(struct super_block *sb,
seq = mmp_new_seq(); seq = mmp_new_seq();
mmp->mmp_seq = cpu_to_le32(seq); mmp->mmp_seq = cpu_to_le32(seq);
retval = write_mmp_block(bh); retval = write_mmp_block(sb, bh);
if (retval) if (retval)
goto failed; goto failed;
......
This diff is collapsed.
...@@ -161,6 +161,8 @@ static struct ext4_new_flex_group_data *alloc_flex_gd(unsigned long flexbg_size) ...@@ -161,6 +161,8 @@ static struct ext4_new_flex_group_data *alloc_flex_gd(unsigned long flexbg_size)
if (flex_gd == NULL) if (flex_gd == NULL)
goto out3; goto out3;
if (flexbg_size >= UINT_MAX / sizeof(struct ext4_new_flex_group_data))
goto out2;
flex_gd->count = flexbg_size; flex_gd->count = flexbg_size;
flex_gd->groups = kmalloc(sizeof(struct ext4_new_group_data) * flex_gd->groups = kmalloc(sizeof(struct ext4_new_group_data) *
...@@ -796,7 +798,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, ...@@ -796,7 +798,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
ext4_kvfree(o_group_desc); ext4_kvfree(o_group_desc);
le16_add_cpu(&es->s_reserved_gdt_blocks, -1); le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); err = ext4_handle_dirty_super_now(handle, sb);
if (err) if (err)
ext4_std_error(sb, err); ext4_std_error(sb, err);
...@@ -968,6 +970,8 @@ static void update_backups(struct super_block *sb, ...@@ -968,6 +970,8 @@ static void update_backups(struct super_block *sb,
goto exit_err; goto exit_err;
} }
ext4_superblock_csum_set(sb, (struct ext4_super_block *)data);
while ((group = ext4_list_backups(sb, &three, &five, &seven)) < last) { while ((group = ext4_list_backups(sb, &three, &five, &seven)) < last) {
struct buffer_head *bh; struct buffer_head *bh;
...@@ -1067,6 +1071,54 @@ static int ext4_add_new_descs(handle_t *handle, struct super_block *sb, ...@@ -1067,6 +1071,54 @@ static int ext4_add_new_descs(handle_t *handle, struct super_block *sb,
return err; return err;
} }
static struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block)
{
struct buffer_head *bh = sb_getblk(sb, block);
if (!bh)
return NULL;
if (bitmap_uptodate(bh))
return bh;
lock_buffer(bh);
if (bh_submit_read(bh) < 0) {
unlock_buffer(bh);
brelse(bh);
return NULL;
}
unlock_buffer(bh);
return bh;
}
static int ext4_set_bitmap_checksums(struct super_block *sb,
ext4_group_t group,
struct ext4_group_desc *gdp,
struct ext4_new_group_data *group_data)
{
struct buffer_head *bh;
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return 0;
bh = ext4_get_bitmap(sb, group_data->inode_bitmap);
if (!bh)
return -EIO;
ext4_inode_bitmap_csum_set(sb, group, gdp, bh,
EXT4_INODES_PER_GROUP(sb) / 8);
brelse(bh);
bh = ext4_get_bitmap(sb, group_data->block_bitmap);
if (!bh)
return -EIO;
ext4_block_bitmap_csum_set(sb, group, gdp, bh,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
brelse(bh);
return 0;
}
/* /*
* ext4_setup_new_descs() will set up the group descriptor descriptors of a flex bg * ext4_setup_new_descs() will set up the group descriptor descriptors of a flex bg
*/ */
...@@ -1093,18 +1145,24 @@ static int ext4_setup_new_descs(handle_t *handle, struct super_block *sb, ...@@ -1093,18 +1145,24 @@ static int ext4_setup_new_descs(handle_t *handle, struct super_block *sb,
*/ */
gdb_bh = sbi->s_group_desc[gdb_num]; gdb_bh = sbi->s_group_desc[gdb_num];
/* Update group descriptor block for new group */ /* Update group descriptor block for new group */
gdp = (struct ext4_group_desc *)((char *)gdb_bh->b_data + gdp = (struct ext4_group_desc *)(gdb_bh->b_data +
gdb_off * EXT4_DESC_SIZE(sb)); gdb_off * EXT4_DESC_SIZE(sb));
memset(gdp, 0, EXT4_DESC_SIZE(sb)); memset(gdp, 0, EXT4_DESC_SIZE(sb));
ext4_block_bitmap_set(sb, gdp, group_data->block_bitmap); ext4_block_bitmap_set(sb, gdp, group_data->block_bitmap);
ext4_inode_bitmap_set(sb, gdp, group_data->inode_bitmap); ext4_inode_bitmap_set(sb, gdp, group_data->inode_bitmap);
err = ext4_set_bitmap_checksums(sb, group, gdp, group_data);
if (err) {
ext4_std_error(sb, err);
break;
}
ext4_inode_table_set(sb, gdp, group_data->inode_table); ext4_inode_table_set(sb, gdp, group_data->inode_table);
ext4_free_group_clusters_set(sb, gdp, ext4_free_group_clusters_set(sb, gdp,
EXT4_B2C(sbi, group_data->free_blocks_count)); EXT4_B2C(sbi, group_data->free_blocks_count));
ext4_free_inodes_set(sb, gdp, EXT4_INODES_PER_GROUP(sb)); ext4_free_inodes_set(sb, gdp, EXT4_INODES_PER_GROUP(sb));
gdp->bg_flags = cpu_to_le16(*bg_flags); gdp->bg_flags = cpu_to_le16(*bg_flags);
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); ext4_group_desc_csum_set(sb, group, gdp);
err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh); err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
if (unlikely(err)) { if (unlikely(err)) {
...@@ -1343,17 +1401,14 @@ static int ext4_setup_next_flex_gd(struct super_block *sb, ...@@ -1343,17 +1401,14 @@ static int ext4_setup_next_flex_gd(struct super_block *sb,
(1 + ext4_bg_num_gdb(sb, group + i) + (1 + ext4_bg_num_gdb(sb, group + i) +
le16_to_cpu(es->s_reserved_gdt_blocks)) : 0; le16_to_cpu(es->s_reserved_gdt_blocks)) : 0;
group_data[i].free_blocks_count = blocks_per_group - overhead; group_data[i].free_blocks_count = blocks_per_group - overhead;
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, if (ext4_has_group_desc_csum(sb))
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT | flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT |
EXT4_BG_INODE_UNINIT; EXT4_BG_INODE_UNINIT;
else else
flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED; flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED;
} }
if (last_group == n_group && if (last_group == n_group && ext4_has_group_desc_csum(sb))
EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
/* We need to initialize block bitmap of last group. */ /* We need to initialize block bitmap of last group. */
flex_gd->bg_flags[i - 1] &= ~EXT4_BG_BLOCK_UNINIT; flex_gd->bg_flags[i - 1] &= ~EXT4_BG_BLOCK_UNINIT;
......
This diff is collapsed.
...@@ -122,6 +122,58 @@ const struct xattr_handler *ext4_xattr_handlers[] = { ...@@ -122,6 +122,58 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
NULL NULL
}; };
static __le32 ext4_xattr_block_csum(struct inode *inode,
sector_t block_nr,
struct ext4_xattr_header *hdr)
{
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
struct ext4_inode_info *ei = EXT4_I(inode);
__u32 csum, old;
old = hdr->h_checksum;
hdr->h_checksum = 0;
if (le32_to_cpu(hdr->h_refcount) != 1) {
block_nr = cpu_to_le64(block_nr);
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&block_nr,
sizeof(block_nr));
} else
csum = ei->i_csum_seed;
csum = ext4_chksum(sbi, csum, (__u8 *)hdr,
EXT4_BLOCK_SIZE(inode->i_sb));
hdr->h_checksum = old;
return cpu_to_le32(csum);
}
static int ext4_xattr_block_csum_verify(struct inode *inode,
sector_t block_nr,
struct ext4_xattr_header *hdr)
{
if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
(hdr->h_checksum != ext4_xattr_block_csum(inode, block_nr, hdr)))
return 0;
return 1;
}
static void ext4_xattr_block_csum_set(struct inode *inode,
sector_t block_nr,
struct ext4_xattr_header *hdr)
{
if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
return;
hdr->h_checksum = ext4_xattr_block_csum(inode, block_nr, hdr);
}
static inline int ext4_handle_dirty_xattr_block(handle_t *handle,
struct inode *inode,
struct buffer_head *bh)
{
ext4_xattr_block_csum_set(inode, bh->b_blocknr, BHDR(bh));
return ext4_handle_dirty_metadata(handle, inode, bh);
}
static inline const struct xattr_handler * static inline const struct xattr_handler *
ext4_xattr_handler(int name_index) ext4_xattr_handler(int name_index)
{ {
...@@ -156,12 +208,22 @@ ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end) ...@@ -156,12 +208,22 @@ ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end)
} }
static inline int static inline int
ext4_xattr_check_block(struct buffer_head *bh) ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
{ {
int error;
if (buffer_verified(bh))
return 0;
if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) || if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
BHDR(bh)->h_blocks != cpu_to_le32(1)) BHDR(bh)->h_blocks != cpu_to_le32(1))
return -EIO; return -EIO;
return ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size); if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh)))
return -EIO;
error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);
if (!error)
set_buffer_verified(bh);
return error;
} }
static inline int static inline int
...@@ -224,7 +286,7 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name, ...@@ -224,7 +286,7 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
goto cleanup; goto cleanup;
ea_bdebug(bh, "b_count=%d, refcount=%d", ea_bdebug(bh, "b_count=%d, refcount=%d",
atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));
if (ext4_xattr_check_block(bh)) { if (ext4_xattr_check_block(inode, bh)) {
bad_block: bad_block:
EXT4_ERROR_INODE(inode, "bad block %llu", EXT4_ERROR_INODE(inode, "bad block %llu",
EXT4_I(inode)->i_file_acl); EXT4_I(inode)->i_file_acl);
...@@ -369,7 +431,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size) ...@@ -369,7 +431,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size)
goto cleanup; goto cleanup;
ea_bdebug(bh, "b_count=%d, refcount=%d", ea_bdebug(bh, "b_count=%d, refcount=%d",
atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));
if (ext4_xattr_check_block(bh)) { if (ext4_xattr_check_block(inode, bh)) {
EXT4_ERROR_INODE(inode, "bad block %llu", EXT4_ERROR_INODE(inode, "bad block %llu",
EXT4_I(inode)->i_file_acl); EXT4_I(inode)->i_file_acl);
error = -EIO; error = -EIO;
...@@ -492,7 +554,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, ...@@ -492,7 +554,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
if (ce) if (ce)
mb_cache_entry_release(ce); mb_cache_entry_release(ce);
unlock_buffer(bh); unlock_buffer(bh);
error = ext4_handle_dirty_metadata(handle, inode, bh); error = ext4_handle_dirty_xattr_block(handle, inode, bh);
if (IS_SYNC(inode)) if (IS_SYNC(inode))
ext4_handle_sync(handle); ext4_handle_sync(handle);
dquot_free_block(inode, 1); dquot_free_block(inode, 1);
...@@ -662,7 +724,7 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i, ...@@ -662,7 +724,7 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i,
ea_bdebug(bs->bh, "b_count=%d, refcount=%d", ea_bdebug(bs->bh, "b_count=%d, refcount=%d",
atomic_read(&(bs->bh->b_count)), atomic_read(&(bs->bh->b_count)),
le32_to_cpu(BHDR(bs->bh)->h_refcount)); le32_to_cpu(BHDR(bs->bh)->h_refcount));
if (ext4_xattr_check_block(bs->bh)) { if (ext4_xattr_check_block(inode, bs->bh)) {
EXT4_ERROR_INODE(inode, "bad block %llu", EXT4_ERROR_INODE(inode, "bad block %llu",
EXT4_I(inode)->i_file_acl); EXT4_I(inode)->i_file_acl);
error = -EIO; error = -EIO;
...@@ -725,7 +787,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, ...@@ -725,7 +787,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
if (error == -EIO) if (error == -EIO)
goto bad_block; goto bad_block;
if (!error) if (!error)
error = ext4_handle_dirty_metadata(handle, error = ext4_handle_dirty_xattr_block(handle,
inode, inode,
bs->bh); bs->bh);
if (error) if (error)
...@@ -796,7 +858,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, ...@@ -796,7 +858,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
ea_bdebug(new_bh, "reusing; refcount now=%d", ea_bdebug(new_bh, "reusing; refcount now=%d",
le32_to_cpu(BHDR(new_bh)->h_refcount)); le32_to_cpu(BHDR(new_bh)->h_refcount));
unlock_buffer(new_bh); unlock_buffer(new_bh);
error = ext4_handle_dirty_metadata(handle, error = ext4_handle_dirty_xattr_block(handle,
inode, inode,
new_bh); new_bh);
if (error) if (error)
...@@ -855,7 +917,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, ...@@ -855,7 +917,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
set_buffer_uptodate(new_bh); set_buffer_uptodate(new_bh);
unlock_buffer(new_bh); unlock_buffer(new_bh);
ext4_xattr_cache_insert(new_bh); ext4_xattr_cache_insert(new_bh);
error = ext4_handle_dirty_metadata(handle, error = ext4_handle_dirty_xattr_block(handle,
inode, new_bh); inode, new_bh);
if (error) if (error)
goto cleanup; goto cleanup;
...@@ -1193,7 +1255,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, ...@@ -1193,7 +1255,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
error = -EIO; error = -EIO;
if (!bh) if (!bh)
goto cleanup; goto cleanup;
if (ext4_xattr_check_block(bh)) { if (ext4_xattr_check_block(inode, bh)) {
EXT4_ERROR_INODE(inode, "bad block %llu", EXT4_ERROR_INODE(inode, "bad block %llu",
EXT4_I(inode)->i_file_acl); EXT4_I(inode)->i_file_acl);
error = -EIO; error = -EIO;
......
...@@ -27,7 +27,9 @@ struct ext4_xattr_header { ...@@ -27,7 +27,9 @@ struct ext4_xattr_header {
__le32 h_refcount; /* reference count */ __le32 h_refcount; /* reference count */
__le32 h_blocks; /* number of disk blocks used */ __le32 h_blocks; /* number of disk blocks used */
__le32 h_hash; /* hash value of all attributes */ __le32 h_hash; /* hash value of all attributes */
__u32 h_reserved[4]; /* zero right now */ __le32 h_checksum; /* crc32c(uuid+id+xattrblock) */
/* id = inum if refcount=1, blknum otherwise */
__u32 h_reserved[3]; /* zero right now */
}; };
struct ext4_xattr_ibody_header { struct ext4_xattr_ibody_header {
......
config JBD2 config JBD2
tristate tristate
select CRC32 select CRC32
select CRYPTO
select CRYPTO_CRC32C
help help
This is a generic journaling layer for block devices that support This is a generic journaling layer for block devices that support
both 32-bit and 64-bit block numbers. It is currently used by both 32-bit and 64-bit block numbers. It is currently used by
......
...@@ -85,6 +85,24 @@ static void release_buffer_page(struct buffer_head *bh) ...@@ -85,6 +85,24 @@ static void release_buffer_page(struct buffer_head *bh)
__brelse(bh); __brelse(bh);
} }
static void jbd2_commit_block_csum_set(journal_t *j,
struct journal_head *descriptor)
{
struct commit_header *h;
__u32 csum;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return;
h = (struct commit_header *)(jh2bh(descriptor)->b_data);
h->h_chksum_type = 0;
h->h_chksum_size = 0;
h->h_chksum[0] = 0;
csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data,
j->j_blocksize);
h->h_chksum[0] = cpu_to_be32(csum);
}
/* /*
* Done it all: now submit the commit record. We should have * Done it all: now submit the commit record. We should have
* cleaned up our previous buffers by now, so if we are in abort * cleaned up our previous buffers by now, so if we are in abort
...@@ -128,6 +146,7 @@ static int journal_submit_commit_record(journal_t *journal, ...@@ -128,6 +146,7 @@ static int journal_submit_commit_record(journal_t *journal,
tmp->h_chksum_size = JBD2_CRC32_CHKSUM_SIZE; tmp->h_chksum_size = JBD2_CRC32_CHKSUM_SIZE;
tmp->h_chksum[0] = cpu_to_be32(crc32_sum); tmp->h_chksum[0] = cpu_to_be32(crc32_sum);
} }
jbd2_commit_block_csum_set(journal, descriptor);
JBUFFER_TRACE(descriptor, "submit commit block"); JBUFFER_TRACE(descriptor, "submit commit block");
lock_buffer(bh); lock_buffer(bh);
...@@ -301,6 +320,44 @@ static void write_tag_block(int tag_bytes, journal_block_tag_t *tag, ...@@ -301,6 +320,44 @@ static void write_tag_block(int tag_bytes, journal_block_tag_t *tag,
tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1); tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1);
} }
static void jbd2_descr_block_csum_set(journal_t *j,
struct journal_head *descriptor)
{
struct jbd2_journal_block_tail *tail;
__u32 csum;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return;
tail = (struct jbd2_journal_block_tail *)
(jh2bh(descriptor)->b_data + j->j_blocksize -
sizeof(struct jbd2_journal_block_tail));
tail->t_checksum = 0;
csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data,
j->j_blocksize);
tail->t_checksum = cpu_to_be32(csum);
}
static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag,
struct buffer_head *bh, __u32 sequence)
{
struct page *page = bh->b_page;
__u8 *addr;
__u32 csum;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return;
sequence = cpu_to_be32(sequence);
addr = kmap_atomic(page, KM_USER0);
csum = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&sequence,
sizeof(sequence));
csum = jbd2_chksum(j, csum, addr + offset_in_page(bh->b_data),
bh->b_size);
kunmap_atomic(addr, KM_USER0);
tag->t_checksum = cpu_to_be32(csum);
}
/* /*
* jbd2_journal_commit_transaction * jbd2_journal_commit_transaction
* *
...@@ -334,6 +391,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -334,6 +391,10 @@ void jbd2_journal_commit_transaction(journal_t *journal)
unsigned long first_block; unsigned long first_block;
tid_t first_tid; tid_t first_tid;
int update_tail; int update_tail;
int csum_size = 0;
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
csum_size = sizeof(struct jbd2_journal_block_tail);
/* /*
* First job: lock down the current transaction and wait for * First job: lock down the current transaction and wait for
...@@ -627,7 +688,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -627,7 +688,9 @@ void jbd2_journal_commit_transaction(journal_t *journal)
tag = (journal_block_tag_t *) tagp; tag = (journal_block_tag_t *) tagp;
write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr); write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr);
tag->t_flags = cpu_to_be32(tag_flag); tag->t_flags = cpu_to_be16(tag_flag);
jbd2_block_tag_csum_set(journal, tag, jh2bh(new_jh),
commit_transaction->t_tid);
tagp += tag_bytes; tagp += tag_bytes;
space_left -= tag_bytes; space_left -= tag_bytes;
...@@ -643,7 +706,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -643,7 +706,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
if (bufs == journal->j_wbufsize || if (bufs == journal->j_wbufsize ||
commit_transaction->t_buffers == NULL || commit_transaction->t_buffers == NULL ||
space_left < tag_bytes + 16) { space_left < tag_bytes + 16 + csum_size) {
jbd_debug(4, "JBD2: Submit %d IOs\n", bufs); jbd_debug(4, "JBD2: Submit %d IOs\n", bufs);
...@@ -651,8 +714,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -651,8 +714,9 @@ void jbd2_journal_commit_transaction(journal_t *journal)
submitting the IOs. "tag" still points to submitting the IOs. "tag" still points to
the last tag we set up. */ the last tag we set up. */
tag->t_flags |= cpu_to_be32(JBD2_FLAG_LAST_TAG); tag->t_flags |= cpu_to_be16(JBD2_FLAG_LAST_TAG);
jbd2_descr_block_csum_set(journal, descriptor);
start_journal_io: start_journal_io:
for (i = 0; i < bufs; i++) { for (i = 0; i < bufs; i++) {
struct buffer_head *bh = wbuf[i]; struct buffer_head *bh = wbuf[i];
......
...@@ -97,6 +97,43 @@ EXPORT_SYMBOL(jbd2_inode_cache); ...@@ -97,6 +97,43 @@ EXPORT_SYMBOL(jbd2_inode_cache);
static void __journal_abort_soft (journal_t *journal, int errno); static void __journal_abort_soft (journal_t *journal, int errno);
static int jbd2_journal_create_slab(size_t slab_size); static int jbd2_journal_create_slab(size_t slab_size);
/* Checksumming functions */
int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb)
{
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
return sb->s_checksum_type == JBD2_CRC32C_CHKSUM;
}
static __u32 jbd2_superblock_csum(journal_t *j, journal_superblock_t *sb)
{
__u32 csum, old_csum;
old_csum = sb->s_checksum;
sb->s_checksum = 0;
csum = jbd2_chksum(j, ~0, (char *)sb, sizeof(journal_superblock_t));
sb->s_checksum = old_csum;
return cpu_to_be32(csum);
}
int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb)
{
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
return sb->s_checksum == jbd2_superblock_csum(j, sb);
}
void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb)
{
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return;
sb->s_checksum = jbd2_superblock_csum(j, sb);
}
/* /*
* Helper function used to manage commit timeouts * Helper function used to manage commit timeouts
*/ */
...@@ -1348,6 +1385,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal) ...@@ -1348,6 +1385,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal)
jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", jbd_debug(1, "JBD2: updating superblock error (errno %d)\n",
journal->j_errno); journal->j_errno);
sb->s_errno = cpu_to_be32(journal->j_errno); sb->s_errno = cpu_to_be32(journal->j_errno);
jbd2_superblock_csum_set(journal, sb);
read_unlock(&journal->j_state_lock); read_unlock(&journal->j_state_lock);
jbd2_write_superblock(journal, WRITE_SYNC); jbd2_write_superblock(journal, WRITE_SYNC);
...@@ -1376,6 +1414,9 @@ static int journal_get_superblock(journal_t *journal) ...@@ -1376,6 +1414,9 @@ static int journal_get_superblock(journal_t *journal)
} }
} }
if (buffer_verified(bh))
return 0;
sb = journal->j_superblock; sb = journal->j_superblock;
err = -EINVAL; err = -EINVAL;
...@@ -1413,6 +1454,43 @@ static int journal_get_superblock(journal_t *journal) ...@@ -1413,6 +1454,43 @@ static int journal_get_superblock(journal_t *journal)
goto out; goto out;
} }
if (JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM) &&
JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
/* Can't have checksum v1 and v2 on at the same time! */
printk(KERN_ERR "JBD: Can't enable checksumming v1 and v2 "
"at the same time!\n");
goto out;
}
if (!jbd2_verify_csum_type(journal, sb)) {
printk(KERN_ERR "JBD: Unknown checksum type\n");
goto out;
}
/* Load the checksum driver */
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);
if (IS_ERR(journal->j_chksum_driver)) {
printk(KERN_ERR "JBD: Cannot load crc32c driver.\n");
err = PTR_ERR(journal->j_chksum_driver);
journal->j_chksum_driver = NULL;
goto out;
}
}
/* Check superblock checksum */
if (!jbd2_superblock_csum_verify(journal, sb)) {
printk(KERN_ERR "JBD: journal checksum error\n");
goto out;
}
/* Precompute checksum seed for all metadata */
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid,
sizeof(sb->s_uuid));
set_buffer_verified(bh);
return 0; return 0;
out: out:
...@@ -1564,6 +1642,8 @@ int jbd2_journal_destroy(journal_t *journal) ...@@ -1564,6 +1642,8 @@ int jbd2_journal_destroy(journal_t *journal)
iput(journal->j_inode); iput(journal->j_inode);
if (journal->j_revoke) if (journal->j_revoke)
jbd2_journal_destroy_revoke(journal); jbd2_journal_destroy_revoke(journal);
if (journal->j_chksum_driver)
crypto_free_shash(journal->j_chksum_driver);
kfree(journal->j_wbuf); kfree(journal->j_wbuf);
kfree(journal); kfree(journal);
...@@ -1653,6 +1733,10 @@ int jbd2_journal_check_available_features (journal_t *journal, unsigned long com ...@@ -1653,6 +1733,10 @@ int jbd2_journal_check_available_features (journal_t *journal, unsigned long com
int jbd2_journal_set_features (journal_t *journal, unsigned long compat, int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
unsigned long ro, unsigned long incompat) unsigned long ro, unsigned long incompat)
{ {
#define INCOMPAT_FEATURE_ON(f) \
((incompat & (f)) && !(sb->s_feature_incompat & cpu_to_be32(f)))
#define COMPAT_FEATURE_ON(f) \
((compat & (f)) && !(sb->s_feature_compat & cpu_to_be32(f)))
journal_superblock_t *sb; journal_superblock_t *sb;
if (jbd2_journal_check_used_features(journal, compat, ro, incompat)) if (jbd2_journal_check_used_features(journal, compat, ro, incompat))
...@@ -1661,16 +1745,54 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, ...@@ -1661,16 +1745,54 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
if (!jbd2_journal_check_available_features(journal, compat, ro, incompat)) if (!jbd2_journal_check_available_features(journal, compat, ro, incompat))
return 0; return 0;
/* Asking for checksumming v2 and v1? Only give them v2. */
if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2 &&
compat & JBD2_FEATURE_COMPAT_CHECKSUM)
compat &= ~JBD2_FEATURE_COMPAT_CHECKSUM;
jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n", jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n",
compat, ro, incompat); compat, ro, incompat);
sb = journal->j_superblock; sb = journal->j_superblock;
/* If enabling v2 checksums, update superblock */
if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
sb->s_checksum_type = JBD2_CRC32C_CHKSUM;
sb->s_feature_compat &=
~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
/* Load the checksum driver */
if (journal->j_chksum_driver == NULL) {
journal->j_chksum_driver = crypto_alloc_shash("crc32c",
0, 0);
if (IS_ERR(journal->j_chksum_driver)) {
printk(KERN_ERR "JBD: Cannot load crc32c "
"driver.\n");
journal->j_chksum_driver = NULL;
return 0;
}
}
/* Precompute checksum seed for all metadata */
if (JBD2_HAS_INCOMPAT_FEATURE(journal,
JBD2_FEATURE_INCOMPAT_CSUM_V2))
journal->j_csum_seed = jbd2_chksum(journal, ~0,
sb->s_uuid,
sizeof(sb->s_uuid));
}
/* If enabling v1 checksums, downgrade superblock */
if (COMPAT_FEATURE_ON(JBD2_FEATURE_COMPAT_CHECKSUM))
sb->s_feature_incompat &=
~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2);
sb->s_feature_compat |= cpu_to_be32(compat); sb->s_feature_compat |= cpu_to_be32(compat);
sb->s_feature_ro_compat |= cpu_to_be32(ro); sb->s_feature_ro_compat |= cpu_to_be32(ro);
sb->s_feature_incompat |= cpu_to_be32(incompat); sb->s_feature_incompat |= cpu_to_be32(incompat);
return 1; return 1;
#undef COMPAT_FEATURE_ON
#undef INCOMPAT_FEATURE_ON
} }
/* /*
...@@ -1975,10 +2097,16 @@ int jbd2_journal_blocks_per_page(struct inode *inode) ...@@ -1975,10 +2097,16 @@ int jbd2_journal_blocks_per_page(struct inode *inode)
*/ */
size_t journal_tag_bytes(journal_t *journal) size_t journal_tag_bytes(journal_t *journal)
{ {
journal_block_tag_t tag;
size_t x = 0;
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
x += sizeof(tag.t_checksum);
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
return JBD2_TAG_SIZE64; return x + JBD2_TAG_SIZE64;
else else
return JBD2_TAG_SIZE32; return x + JBD2_TAG_SIZE32;
} }
/* /*
......
...@@ -174,6 +174,25 @@ static int jread(struct buffer_head **bhp, journal_t *journal, ...@@ -174,6 +174,25 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
return 0; return 0;
} }
static int jbd2_descr_block_csum_verify(journal_t *j,
void *buf)
{
struct jbd2_journal_block_tail *tail;
__u32 provided, calculated;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
tail = (struct jbd2_journal_block_tail *)(buf + j->j_blocksize -
sizeof(struct jbd2_journal_block_tail));
provided = tail->t_checksum;
tail->t_checksum = 0;
calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
tail->t_checksum = provided;
provided = be32_to_cpu(provided);
return provided == calculated;
}
/* /*
* Count the number of in-use tags in a journal descriptor block. * Count the number of in-use tags in a journal descriptor block.
...@@ -186,6 +205,9 @@ static int count_tags(journal_t *journal, struct buffer_head *bh) ...@@ -186,6 +205,9 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
int nr = 0, size = journal->j_blocksize; int nr = 0, size = journal->j_blocksize;
int tag_bytes = journal_tag_bytes(journal); int tag_bytes = journal_tag_bytes(journal);
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
size -= sizeof(struct jbd2_journal_block_tail);
tagp = &bh->b_data[sizeof(journal_header_t)]; tagp = &bh->b_data[sizeof(journal_header_t)];
while ((tagp - bh->b_data + tag_bytes) <= size) { while ((tagp - bh->b_data + tag_bytes) <= size) {
...@@ -193,10 +215,10 @@ static int count_tags(journal_t *journal, struct buffer_head *bh) ...@@ -193,10 +215,10 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
nr++; nr++;
tagp += tag_bytes; tagp += tag_bytes;
if (!(tag->t_flags & cpu_to_be32(JBD2_FLAG_SAME_UUID))) if (!(tag->t_flags & cpu_to_be16(JBD2_FLAG_SAME_UUID)))
tagp += 16; tagp += 16;
if (tag->t_flags & cpu_to_be32(JBD2_FLAG_LAST_TAG)) if (tag->t_flags & cpu_to_be16(JBD2_FLAG_LAST_TAG))
break; break;
} }
...@@ -353,6 +375,41 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh, ...@@ -353,6 +375,41 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,
return 0; return 0;
} }
static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
{
struct commit_header *h;
__u32 provided, calculated;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
h = buf;
provided = h->h_chksum[0];
h->h_chksum[0] = 0;
calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
h->h_chksum[0] = provided;
provided = be32_to_cpu(provided);
return provided == calculated;
}
static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
void *buf, __u32 sequence)
{
__u32 provided, calculated;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
sequence = cpu_to_be32(sequence);
calculated = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&sequence,
sizeof(sequence));
calculated = jbd2_chksum(j, calculated, buf, j->j_blocksize);
provided = be32_to_cpu(tag->t_checksum);
return provided == cpu_to_be32(calculated);
}
static int do_one_pass(journal_t *journal, static int do_one_pass(journal_t *journal,
struct recovery_info *info, enum passtype pass) struct recovery_info *info, enum passtype pass)
{ {
...@@ -366,6 +423,7 @@ static int do_one_pass(journal_t *journal, ...@@ -366,6 +423,7 @@ static int do_one_pass(journal_t *journal,
int blocktype; int blocktype;
int tag_bytes = journal_tag_bytes(journal); int tag_bytes = journal_tag_bytes(journal);
__u32 crc32_sum = ~0; /* Transactional Checksums */ __u32 crc32_sum = ~0; /* Transactional Checksums */
int descr_csum_size = 0;
/* /*
* First thing is to establish what we expect to find in the log * First thing is to establish what we expect to find in the log
...@@ -451,6 +509,18 @@ static int do_one_pass(journal_t *journal, ...@@ -451,6 +509,18 @@ static int do_one_pass(journal_t *journal,
switch(blocktype) { switch(blocktype) {
case JBD2_DESCRIPTOR_BLOCK: case JBD2_DESCRIPTOR_BLOCK:
/* Verify checksum first */
if (JBD2_HAS_INCOMPAT_FEATURE(journal,
JBD2_FEATURE_INCOMPAT_CSUM_V2))
descr_csum_size =
sizeof(struct jbd2_journal_block_tail);
if (descr_csum_size > 0 &&
!jbd2_descr_block_csum_verify(journal,
bh->b_data)) {
err = -EIO;
goto failed;
}
/* If it is a valid descriptor block, replay it /* If it is a valid descriptor block, replay it
* in pass REPLAY; if journal_checksums enabled, then * in pass REPLAY; if journal_checksums enabled, then
* calculate checksums in PASS_SCAN, otherwise, * calculate checksums in PASS_SCAN, otherwise,
...@@ -481,11 +551,11 @@ static int do_one_pass(journal_t *journal, ...@@ -481,11 +551,11 @@ static int do_one_pass(journal_t *journal,
tagp = &bh->b_data[sizeof(journal_header_t)]; tagp = &bh->b_data[sizeof(journal_header_t)];
while ((tagp - bh->b_data + tag_bytes) while ((tagp - bh->b_data + tag_bytes)
<= journal->j_blocksize) { <= journal->j_blocksize - descr_csum_size) {
unsigned long io_block; unsigned long io_block;
tag = (journal_block_tag_t *) tagp; tag = (journal_block_tag_t *) tagp;
flags = be32_to_cpu(tag->t_flags); flags = be16_to_cpu(tag->t_flags);
io_block = next_log_block++; io_block = next_log_block++;
wrap(journal, next_log_block); wrap(journal, next_log_block);
...@@ -516,6 +586,19 @@ static int do_one_pass(journal_t *journal, ...@@ -516,6 +586,19 @@ static int do_one_pass(journal_t *journal,
goto skip_write; goto skip_write;
} }
/* Look for block corruption */
if (!jbd2_block_tag_csum_verify(
journal, tag, obh->b_data,
be32_to_cpu(tmp->h_sequence))) {
brelse(obh);
success = -EIO;
printk(KERN_ERR "JBD: Invalid "
"checksum recovering "
"block %llu in log\n",
blocknr);
continue;
}
/* Find a buffer for the new /* Find a buffer for the new
* data being restored */ * data being restored */
nbh = __getblk(journal->j_fs_dev, nbh = __getblk(journal->j_fs_dev,
...@@ -650,6 +733,19 @@ static int do_one_pass(journal_t *journal, ...@@ -650,6 +733,19 @@ static int do_one_pass(journal_t *journal,
} }
crc32_sum = ~0; crc32_sum = ~0;
} }
if (pass == PASS_SCAN &&
!jbd2_commit_block_csum_verify(journal,
bh->b_data)) {
info->end_transaction = next_commit_ID;
if (!JBD2_HAS_INCOMPAT_FEATURE(journal,
JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
journal->j_failed_commit =
next_commit_ID;
brelse(bh);
break;
}
}
brelse(bh); brelse(bh);
next_commit_ID++; next_commit_ID++;
continue; continue;
...@@ -706,6 +802,25 @@ static int do_one_pass(journal_t *journal, ...@@ -706,6 +802,25 @@ static int do_one_pass(journal_t *journal,
return err; return err;
} }
static int jbd2_revoke_block_csum_verify(journal_t *j,
void *buf)
{
struct jbd2_journal_revoke_tail *tail;
__u32 provided, calculated;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
tail = (struct jbd2_journal_revoke_tail *)(buf + j->j_blocksize -
sizeof(struct jbd2_journal_revoke_tail));
provided = tail->r_checksum;
tail->r_checksum = 0;
calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
tail->r_checksum = provided;
provided = be32_to_cpu(provided);
return provided == calculated;
}
/* Scan a revoke record, marking all blocks mentioned as revoked. */ /* Scan a revoke record, marking all blocks mentioned as revoked. */
...@@ -720,6 +835,9 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, ...@@ -720,6 +835,9 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
offset = sizeof(jbd2_journal_revoke_header_t); offset = sizeof(jbd2_journal_revoke_header_t);
max = be32_to_cpu(header->r_count); max = be32_to_cpu(header->r_count);
if (!jbd2_revoke_block_csum_verify(journal, header))
return -EINVAL;
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;
......
...@@ -578,6 +578,7 @@ static void write_one_revoke_record(journal_t *journal, ...@@ -578,6 +578,7 @@ static void write_one_revoke_record(journal_t *journal,
struct jbd2_revoke_record_s *record, struct jbd2_revoke_record_s *record,
int write_op) int write_op)
{ {
int csum_size = 0;
struct journal_head *descriptor; struct journal_head *descriptor;
int offset; int offset;
journal_header_t *header; journal_header_t *header;
...@@ -592,9 +593,13 @@ static void write_one_revoke_record(journal_t *journal, ...@@ -592,9 +593,13 @@ static void write_one_revoke_record(journal_t *journal,
descriptor = *descriptorp; descriptor = *descriptorp;
offset = *offsetp; offset = *offsetp;
/* Do we need to leave space at the end for a checksum? */
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
csum_size = sizeof(struct jbd2_journal_revoke_tail);
/* 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) { if (offset >= journal->j_blocksize - csum_size) {
flush_descriptor(journal, descriptor, offset, write_op); flush_descriptor(journal, descriptor, offset, write_op);
descriptor = NULL; descriptor = NULL;
} }
...@@ -631,6 +636,24 @@ static void write_one_revoke_record(journal_t *journal, ...@@ -631,6 +636,24 @@ static void write_one_revoke_record(journal_t *journal,
*offsetp = offset; *offsetp = offset;
} }
static void jbd2_revoke_csum_set(journal_t *j,
struct journal_head *descriptor)
{
struct jbd2_journal_revoke_tail *tail;
__u32 csum;
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return;
tail = (struct jbd2_journal_revoke_tail *)
(jh2bh(descriptor)->b_data + j->j_blocksize -
sizeof(struct jbd2_journal_revoke_tail));
tail->r_checksum = 0;
csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data,
j->j_blocksize);
tail->r_checksum = cpu_to_be32(csum);
}
/* /*
* Flush a revoke descriptor out to the journal. If we are aborting, * Flush a revoke descriptor out to the journal. If we are aborting,
* this is a noop; otherwise we are generating a buffer which needs to * this is a noop; otherwise we are generating a buffer which needs to
...@@ -652,6 +675,8 @@ static void flush_descriptor(journal_t *journal, ...@@ -652,6 +675,8 @@ static void flush_descriptor(journal_t *journal,
header = (jbd2_journal_revoke_header_t *) jh2bh(descriptor)->b_data; header = (jbd2_journal_revoke_header_t *) jh2bh(descriptor)->b_data;
header->r_count = cpu_to_be32(offset); header->r_count = cpu_to_be32(offset);
jbd2_revoke_csum_set(journal, descriptor);
set_buffer_jwrite(bh); set_buffer_jwrite(bh);
BUFFER_TRACE(bh, "write"); BUFFER_TRACE(bh, "write");
set_buffer_dirty(bh); set_buffer_dirty(bh);
......
...@@ -162,8 +162,8 @@ static int start_this_handle(journal_t *journal, handle_t *handle, ...@@ -162,8 +162,8 @@ static int start_this_handle(journal_t *journal, handle_t *handle,
alloc_transaction: alloc_transaction:
if (!journal->j_running_transaction) { if (!journal->j_running_transaction) {
new_transaction = kmem_cache_alloc(transaction_cache, new_transaction = kmem_cache_zalloc(transaction_cache,
gfp_mask | __GFP_ZERO); gfp_mask);
if (!new_transaction) { if (!new_transaction) {
/* /*
* If __GFP_FS is not present, then we may be * If __GFP_FS is not present, then we may be
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <crypto/hash.h>
#endif #endif
#define journal_oom_retry 1 #define journal_oom_retry 1
...@@ -147,12 +148,24 @@ typedef struct journal_header_s ...@@ -147,12 +148,24 @@ typedef struct journal_header_s
#define JBD2_CRC32_CHKSUM 1 #define JBD2_CRC32_CHKSUM 1
#define JBD2_MD5_CHKSUM 2 #define JBD2_MD5_CHKSUM 2
#define JBD2_SHA1_CHKSUM 3 #define JBD2_SHA1_CHKSUM 3
#define JBD2_CRC32C_CHKSUM 4
#define JBD2_CRC32_CHKSUM_SIZE 4 #define JBD2_CRC32_CHKSUM_SIZE 4
#define JBD2_CHECKSUM_BYTES (32 / sizeof(u32)) #define JBD2_CHECKSUM_BYTES (32 / sizeof(u32))
/* /*
* Commit block header for storing transactional checksums: * Commit block header for storing transactional checksums:
*
* NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
* fields are used to store a checksum of the descriptor and data blocks.
*
* If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
* field is used to store crc32c(uuid+commit_block). Each journal metadata
* block gets its own checksum, and data block checksums are stored in
* journal_block_tag (in the descriptor). The other h_chksum* fields are
* not used.
*
* Checksum v1 and v2 are mutually exclusive features.
*/ */
struct commit_header { struct commit_header {
__be32 h_magic; __be32 h_magic;
...@@ -175,13 +188,19 @@ struct commit_header { ...@@ -175,13 +188,19 @@ struct commit_header {
typedef struct journal_block_tag_s typedef struct journal_block_tag_s
{ {
__be32 t_blocknr; /* The on-disk block number */ __be32 t_blocknr; /* The on-disk block number */
__be32 t_flags; /* See below */ __be16 t_checksum; /* truncated crc32c(uuid+seq+block) */
__be16 t_flags; /* See below */
__be32 t_blocknr_high; /* most-significant high 32bits. */ __be32 t_blocknr_high; /* most-significant high 32bits. */
} journal_block_tag_t; } journal_block_tag_t;
#define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high)) #define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high))
#define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t)) #define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t))
/* Tail of descriptor block, for checksumming */
struct jbd2_journal_block_tail {
__be32 t_checksum; /* crc32c(uuid+descr_block) */
};
/* /*
* The revoke descriptor: used on disk to describe a series of blocks to * The revoke descriptor: used on disk to describe a series of blocks to
* be revoked from the log * be revoked from the log
...@@ -192,6 +211,10 @@ typedef struct jbd2_journal_revoke_header_s ...@@ -192,6 +211,10 @@ typedef struct jbd2_journal_revoke_header_s
__be32 r_count; /* Count of bytes used in the block */ __be32 r_count; /* Count of bytes used in the block */
} jbd2_journal_revoke_header_t; } jbd2_journal_revoke_header_t;
/* Tail of revoke block, for checksumming */
struct jbd2_journal_revoke_tail {
__be32 r_checksum; /* crc32c(uuid+revoke_block) */
};
/* Definitions for the journal tag flags word: */ /* Definitions for the journal tag flags word: */
#define JBD2_FLAG_ESCAPE 1 /* on-disk block is escaped */ #define JBD2_FLAG_ESCAPE 1 /* on-disk block is escaped */
...@@ -241,7 +264,10 @@ typedef struct journal_superblock_s ...@@ -241,7 +264,10 @@ typedef struct journal_superblock_s
__be32 s_max_trans_data; /* Limit of data blocks per trans. */ __be32 s_max_trans_data; /* Limit of data blocks per trans. */
/* 0x0050 */ /* 0x0050 */
__u32 s_padding[44]; __u8 s_checksum_type; /* checksum type */
__u8 s_padding2[3];
__u32 s_padding[42];
__be32 s_checksum; /* crc32c(superblock) */
/* 0x0100 */ /* 0x0100 */
__u8 s_users[16*48]; /* ids of all fs'es sharing the log */ __u8 s_users[16*48]; /* ids of all fs'es sharing the log */
...@@ -263,13 +289,15 @@ typedef struct journal_superblock_s ...@@ -263,13 +289,15 @@ typedef struct journal_superblock_s
#define JBD2_FEATURE_INCOMPAT_REVOKE 0x00000001 #define JBD2_FEATURE_INCOMPAT_REVOKE 0x00000001
#define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002 #define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002
#define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 #define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
#define JBD2_FEATURE_INCOMPAT_CSUM_V2 0x00000008
/* Features known to this kernel version: */ /* Features known to this kernel version: */
#define JBD2_KNOWN_COMPAT_FEATURES JBD2_FEATURE_COMPAT_CHECKSUM #define JBD2_KNOWN_COMPAT_FEATURES JBD2_FEATURE_COMPAT_CHECKSUM
#define JBD2_KNOWN_ROCOMPAT_FEATURES 0 #define JBD2_KNOWN_ROCOMPAT_FEATURES 0
#define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE | \ #define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE | \
JBD2_FEATURE_INCOMPAT_64BIT | \ JBD2_FEATURE_INCOMPAT_64BIT | \
JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT) JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT | \
JBD2_FEATURE_INCOMPAT_CSUM_V2)
#ifdef __KERNEL__ #ifdef __KERNEL__
...@@ -939,6 +967,12 @@ struct journal_s ...@@ -939,6 +967,12 @@ struct journal_s
* superblock pointer here * superblock pointer here
*/ */
void *j_private; void *j_private;
/* Reference to checksum algorithm driver via cryptoapi */
struct crypto_shash *j_chksum_driver;
/* Precomputed journal UUID checksum for seeding other checksums */
__u32 j_csum_seed;
}; };
/* /*
...@@ -1268,6 +1302,25 @@ static inline int jbd_space_needed(journal_t *journal) ...@@ -1268,6 +1302,25 @@ static inline int jbd_space_needed(journal_t *journal)
extern int jbd_blocks_per_page(struct inode *inode); extern int jbd_blocks_per_page(struct inode *inode);
static inline u32 jbd2_chksum(journal_t *journal, u32 crc,
const void *address, unsigned int length)
{
struct {
struct shash_desc shash;
char ctx[crypto_shash_descsize(journal->j_chksum_driver)];
} desc;
int err;
desc.shash.tfm = journal->j_chksum_driver;
desc.shash.flags = 0;
*(u32 *)desc.ctx = crc;
err = crypto_shash_update(&desc.shash, address, length);
BUG_ON(err);
return *(u32 *)desc.ctx;
}
#ifdef __KERNEL__ #ifdef __KERNEL__
#define buffer_trace_init(bh) do {} while (0) #define buffer_trace_init(bh) do {} while (0)
......
...@@ -12,6 +12,7 @@ enum jbd_state_bits { ...@@ -12,6 +12,7 @@ enum jbd_state_bits {
BH_State, /* Pins most journal_head state */ BH_State, /* Pins most journal_head state */
BH_JournalHead, /* Pins bh->b_private and jh->b_bh */ BH_JournalHead, /* Pins bh->b_private and jh->b_bh */
BH_Unshadow, /* Dummy bit, for BJ_Shadow wakeup filtering */ BH_Unshadow, /* Dummy bit, for BJ_Shadow wakeup filtering */
BH_Verified, /* Metadata block has been verified ok */
BH_JBDPrivateStart, /* First bit available for private use by FS */ BH_JBDPrivateStart, /* First bit available for private use by FS */
}; };
...@@ -24,6 +25,7 @@ TAS_BUFFER_FNS(Revoked, revoked) ...@@ -24,6 +25,7 @@ TAS_BUFFER_FNS(Revoked, revoked)
BUFFER_FNS(RevokeValid, revokevalid) BUFFER_FNS(RevokeValid, revokevalid)
TAS_BUFFER_FNS(RevokeValid, revokevalid) TAS_BUFFER_FNS(RevokeValid, revokevalid)
BUFFER_FNS(Freed, freed) BUFFER_FNS(Freed, freed)
BUFFER_FNS(Verified, verified)
static inline struct buffer_head *jh2bh(struct journal_head *jh) static inline struct buffer_head *jh2bh(struct journal_head *jh)
{ {
......
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