Dedicated metadata read functions
cover Dedicated metadata read functions
The following patch series builds on top of the read_block cleanups to provide dedicated functions to read the various types of ocfs2 metadata. Rather than calling ocfs2_read_block[s]() directly, functions wanting an inode now call ocfs2_read_inode_block(). The same is true for extent blocks, group descriptors, and dirblocks. I will get to xattr blocks later.
The older the metadata type, the more inconsistent the block validation was. Sometimes we didn't even check the signature, other times we checked everything. As part of the changes, we make sure all validation happens inside the dedicated function. The rest of the code no longer has to check for valid blocks.
Patch nine is the best - the validation calls are lifted into ocfs2_read_blocks() via a callback. If validation is requested, it is only performed when the block is actually read from disk. If the block was in cache, no validation is performed. Thus, we can do full validation when a block is read yet not lose performance on subsequent cached reads.
The last thing we do is turn ocfs2_bread() into a cleaner ocfs2_read_virt_blocks(), which is like ocfs2_read_blocks() but with a virtual block number. Not only can ocfs2_read_dir_block() use this, the quota read functions will be able to as well.
Joel Becker (11):
ocfs2: Wrap inode block reads in a dedicated function.
ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.
ocfs2: Consolidate validation of group descriptors.
ocfs2: Wrap group descriptor reads in a dedicated function.
ocfs2: Morph the haphazard OCFS2_IS_VALID_GROUP_DESC() checks.
ocfs2: Wrap extent block reads in a dedicated function.
ocfs2: Wrap dirblock reads in a dedicated function.
ocfs2: Wrap xattr block reads in a dedicated function
ocfs2: Validate metadata only when it's read from disk.
ocfs2: Wrap virtual block reads in ocfs2_read_virt_blocks()
ocfs2: Convert ocfs2_read_dir_block() to ocfs2_read_virt_blocks()
PATCH 1/11 ocfs2: Wrap inode block reads in a dedicated function.
The ocfs2 code currently reads inodes off disk with a simple ocfs2_read_block() call. Each place that does this has a different set of sanity checks it performs. Some check only the signature. A couple validate the block number (the block read vs di->i_blkno). A couple others check for VALID_FL. Only one place validates i_fs_generation. A couple check nothing. Even when an error is found, they don't all do the same thing.
We wrap inode reading into ocfs2_read_inode_block(). This will validate all the above fields, going readonly if they are invalid (they never should be). ocfs2_read_inode_block_full() is provided for the places that want to pass read_block flags. Every caller is passing a struct inode with a valid ip_blkno, so we don't need a separate blkno argument either.
We will remove the validation checks from the rest of the code in a later commit, as they are no longer necessary.
--- fs/ocfs2/alloc.c | 2 +- fs/ocfs2/aops.c | 11 +--- fs/ocfs2/dir.c | 6 +- fs/ocfs2/dlmglue.c | 12 ++--- fs/ocfs2/extent_map.c | 2 +- fs/ocfs2/file.c | 21 ++------ fs/ocfs2/inode.c | 136 +++++++++++++++++++++++++++++++++++-------------- fs/ocfs2/inode.h | 16 +++++- fs/ocfs2/journal.c | 3 +- fs/ocfs2/localalloc.c | 8 ++-- fs/ocfs2/namei.c | 14 +---- fs/ocfs2/symlink.c | 2 +- 12 files changed, 136 insertions(+), 97 deletions(-)
fs/ocfs2/alloc.c +1 -1
@@ -5658,7 +5658,7 @@ static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, goto bail; } - status =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, &bh); + status =ocfs2_read_inode_block (inode, &bh);if (status <0 ) {iput (inode);mlog_errno (status);
fs/ocfs2/aops.c +2 -9
@@ -68,20 +68,13 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock, goto bail; } - status =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, &bh); + status =ocfs2_read_inode_block (inode, &bh);if (status <0 ) {mlog_errno (status); goto bail; } fe = (struct ocfs2_dinode *) bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -mlog (ML_ERROR ,"Invalid dinode #%llu: signature = %.*s\n" , - (unsigned long long )le64_to_cpu (fe->i_blkno),7 , - fe->i_signature); - goto bail; - } -if ((u64 )iblock >=ocfs2_clusters_to_blocks (inode->i_sb,le32_to_cpu (fe->i_clusters))) { mlog(ML_ERROR, "block offset is outside the allocated size: "
@@ -262,7 +255,7 @@ static int ocfs2_readpage_inline(struct inode *inode, struct page *page)BUG_ON (!PageLocked (page));BUG_ON (!(OCFS2_I (inode)->ip_dyn_features &OCFS2_INLINE_DATA_FL )); - ret =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, &di_bh); + ret =ocfs2_read_inode_block (inode, &di_bh);if (ret) {mlog_errno (ret); goto out;
fs/ocfs2/dir.c +3 -3
@@ -231,7 +231,7 @@ static struct buffer_head *ocfs2_find_entry_id(const char *name,struct ocfs2_dinode *di;struct ocfs2_inline_data *data; - ret =ocfs2_read_block (dir,OCFS2_I (dir)->ip_blkno, &di_bh); + ret =ocfs2_read_inode_block (dir, &di_bh);if (ret) {mlog_errno (ret); goto out;
@@ -458,7 +458,7 @@ static inline int ocfs2_delete_entry_id(handle_t *handle,struct ocfs2_dinode *di;struct ocfs2_inline_data *data; - ret =ocfs2_read_block (dir,OCFS2_I (dir)->ip_blkno, &di_bh); + ret =ocfs2_read_inode_block (dir, &di_bh);if (ret) {mlog_errno (ret); goto out;
@@ -636,7 +636,7 @@ static int ocfs2_dir_foreach_blk_id(struct inode *inode,struct ocfs2_inline_data *data;struct ocfs2_dir_entry *de; - ret =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, &di_bh); + ret =ocfs2_read_inode_block (inode, &di_bh);if (ret) {mlog (ML_ERROR ,"Unable to read inode block for dir %llu\n" , (unsigned long long )OCFS2_I (inode)->ip_blkno);
fs/ocfs2/dlmglue.c +4 -8
@@ -2024,7 +2024,7 @@ static int ocfs2_inode_lock_update(struct inode *inode, }else {/* Boo, we have to go to disk. */ /* read bh, cast, ocfs2_refresh_inode */ - status =ocfs2_read_block (inode, oi->ip_blkno, bh); + status =ocfs2_read_inode_block (inode, bh);if (status <0 ) {mlog_errno (status); goto bail_refresh;
@@ -2032,18 +2032,14 @@ static int ocfs2_inode_lock_update(struct inode *inode, fe = (struct ocfs2_dinode *) (*bh)->b_data;/* This is a good chance to make sure we're not -* locking an invalid object. +* locking an invalid object. ocfs2_read_inode_block() +* already checked that the inode block is sane. * * We bug on a stale inode here because we checked * above whether it was wiped from disk. The wiping * node provides a guarantee that we receive that * message and can mark the inode before dropping any * locks associated with it. */ -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); - status = -EIO ; - goto bail_refresh; - }mlog_bug_on_msg (inode->i_generation !=le32_to_cpu (fe->i_generation), "Invalid dinode %llu disk generation: %u "
@@ -2085,7 +2081,7 @@ static int ocfs2_assign_bh(struct inode *inode,return 0 ; } - status =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, ret_bh); + status =ocfs2_read_inode_block (inode, ret_bh);if (status <0 )mlog_errno (status);
fs/ocfs2/extent_map.c +1 -1
@@ -630,7 +630,7 @@ int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,if (ret ==0 ) goto out; - ret =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, &di_bh); + ret =ocfs2_read_inode_block (inode, &di_bh);if (ret) {mlog_errno (ret); goto out;
fs/ocfs2/file.c +5 -16
@@ -401,12 +401,9 @@ static int ocfs2_truncate_file(struct inode *inode, (unsigned long long )OCFS2_I (inode)->ip_blkno, (unsigned long long)new_i_size); +/* We trust di_bh because it comes from ocfs2_inode_lock(), which +* already validated it */ fe = (struct ocfs2_dinode *) di_bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); - status = -EIO ; - goto bail; - }mlog_bug_on_msg (le64_to_cpu (fe->i_size) !=i_size_read (inode), "Inode %llu, inode i_size = %lld != di "
@@ -545,18 +542,12 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,*/ BUG_ON (mark_unwritten && !ocfs2_sparse_alloc (osb)); - status =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, &bh); + status =ocfs2_read_inode_block (inode, &bh);if (status <0 ) {mlog_errno (status); goto leave; } - fe = (struct ocfs2_dinode *) bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (inode->i_sb, fe); - status = -EIO ; - goto leave; - } restart_all:BUG_ON (le32_to_cpu (fe->i_clusters) !=OCFS2_I (inode)->ip_clusters);
@@ -1128,9 +1119,8 @@ static int ocfs2_write_remove_suid(struct inode *inode) {int ret;struct buffer_head *bh =NULL ; -struct ocfs2_inode_info *oi =OCFS2_I (inode); - ret =ocfs2_read_block (inode, oi->ip_blkno, &bh); + ret =ocfs2_read_inode_block (inode, &bh);if (ret <0 ) {mlog_errno (ret); goto out;
@@ -1156,8 +1146,7 @@ static int ocfs2_allocate_unwritten_extents(struct inode *inode,struct buffer_head *di_bh =NULL ;if (OCFS2_I (inode)->ip_dyn_features &OCFS2_INLINE_DATA_FL ) { - ret =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, - &di_bh); + ret =ocfs2_read_inode_block (inode, &di_bh);if (ret) {mlog_errno (ret); goto out;
fs/ocfs2/inode.c +98 -38
181 lines not on this page · raw message
fs/ocfs2/inode.h +14 -2
@@ -128,8 +128,8 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, unsigned flags, int sysfile_type);int ocfs2_inode_init_private (struct inode *inode);int ocfs2_inode_revalidate (struct dentry *dentry); -int ocfs2_populate_inode (struct inode *inode,struct ocfs2_dinode *fe, -int create_ino); +void ocfs2_populate_inode (struct inode *inode,struct ocfs2_dinode *fe, +int create_ino);void ocfs2_read_inode (struct inode *inode);void ocfs2_read_inode2 (struct inode *inode,void *opaque); ssize_t ocfs2_rw_direct(int rw, struct file *filp, char *buf,
@@ -153,4 +153,16 @@ static inline blkcnt_t ocfs2_inode_sector_count(struct inode *inode) return (blkcnt_t)(OCFS2_I(inode)->ip_clusters << c_to_s_bits); } +/* Validate that a bh contains a valid inode */ +int ocfs2_validate_inode_block (struct super_block *sb, +struct buffer_head *bh); +/* +* Read an inode block into *bh. If *bh is NULL, a bh will be allocated. +* This is a cached read. The inode will be validated with +* ocfs2_validate_inode_block(). +*/ +int ocfs2_read_inode_block (struct inode *inode,struct buffer_head **bh); +/* The same, but can be passed OCFS2_BH_* flags */ +int ocfs2_read_inode_block_full (struct inode *inode,struct buffer_head **bh, +int flags);#endif /* OCFS2_INODE_H */
fs/ocfs2/journal.c +1 -2
@@ -1135,8 +1135,7 @@ static int ocfs2_read_journal_inode(struct ocfs2_super *osb, }SET_INODE_JOURNAL (inode); - status =ocfs2_read_blocks (inode,OCFS2_I (inode)->ip_blkno,1 , bh, -OCFS2_BH_IGNORE_CACHE ); + status =ocfs2_read_inode_block_full (inode, bh,OCFS2_BH_IGNORE_CACHE );if (status <0 ) {mlog_errno (status); goto bail;
fs/ocfs2/localalloc.c +4 -4
@@ -248,8 +248,8 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb) goto bail; } - status =ocfs2_read_blocks (inode,OCFS2_I (inode)->ip_blkno,1 , - &alloc_bh,OCFS2_BH_IGNORE_CACHE ); + status =ocfs2_read_inode_block_full (inode, &alloc_bh, +OCFS2_BH_IGNORE_CACHE );if (status <0 ) {mlog_errno (status); goto bail;
@@ -459,8 +459,8 @@ int ocfs2_begin_local_alloc_recovery(struct ocfs2_super *osb,mutex_lock (&inode->i_mutex); - status =ocfs2_read_blocks (inode,OCFS2_I (inode)->ip_blkno,1 , - &alloc_bh,OCFS2_BH_IGNORE_CACHE ); + status =ocfs2_read_inode_block_full (inode, &alloc_bh, +OCFS2_BH_IGNORE_CACHE );if (status <0 ) {mlog_errno (status); goto bail;
fs/ocfs2/namei.c +2 -12
@@ -465,15 +465,7 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, goto leave; } -if (ocfs2_populate_inode (inode, fe,1 ) <0 ) { -mlog (ML_ERROR ,"populate inode failed! bh->b_blocknr=%llu, " -"i_blkno=%llu, i_ino=%lu\n" , - (unsigned long long )(*new_fe_bh)->b_blocknr, - (unsigned long long )le64_to_cpu (fe->i_blkno), - inode->i_ino); -BUG (); - } - +ocfs2_populate_inode (inode, fe,1 );ocfs2_inode_set_new (osb, inode);if (!ocfs2_mount_local (osb)) { status =ocfs2_create_new_inode_locks (inode);
@@ -1754,9 +1746,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb,mlog_entry ("(inode->i_ino = %lu)\n" , inode->i_ino); - status =ocfs2_read_block (orphan_dir_inode, -OCFS2_I (orphan_dir_inode)->ip_blkno, - &orphan_dir_bh); + status =ocfs2_read_inode_block (orphan_dir_inode, &orphan_dir_bh);if (status <0 ) {mlog_errno (status); goto leave;
fs/ocfs2/symlink.c +1 -1
@@ -84,7 +84,7 @@ static char *ocfs2_fast_symlink_getlink(struct inode *inode,mlog_entry_void (); - status =ocfs2_read_block (inode,OCFS2_I (inode)->ip_blkno, bh); + status =ocfs2_read_inode_block (inode, bh);if (status <0 ) {mlog_errno (status); link =ERR_PTR (status);
Signature · 1 line
1.5.6.5
PATCH 2/11 ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.
In reply to Joel BeckerRandom places in the code would check a dinode bh to see if it was valid. Not only did they do different levels of validation, they handled errors in different ways.
The previous commit unified inode block reads, validating all block reads in the same place. Thus, these haphazard checks are no longer necessary. Rather than eliminate them, however, we change them to BUG_ON() checks. This ensures the assumptions remain true. All of the code paths to these checks have been audited to ensure they come from a validated inode read.
--- fs/ocfs2/alloc.c | 50 +++++++++++++++++++++----------------------------- fs/ocfs2/journal.c | 17 +++++------------ fs/ocfs2/ocfs2.h | 8 -------- fs/ocfs2/resize.c | 10 ++++------ fs/ocfs2/suballoc.c | 36 ++++++++++++++++-------------------- 5 files changed, 46 insertions(+), 75 deletions(-)
fs/ocfs2/alloc.c +21 -29
@@ -187,20 +187,12 @@ static int ocfs2_dinode_insert_check(struct inode *inode,static int ocfs2_dinode_sanity_check (struct inode *inode,struct ocfs2_extent_tree *et) { -int ret =0 ; -struct ocfs2_dinode *di; +struct ocfs2_dinode *di = et->et_object;BUG_ON (et->et_ops != &ocfs2_dinode_et_ops); +BUG_ON (!OCFS2_IS_VALID_DINODE (di)); - di = et->et_object; -if (!OCFS2_IS_VALID_DINODE (di)) { - ret = -EIO ; -ocfs2_error (inode->i_sb, -"Inode %llu has invalid path root" , - (unsigned long long )OCFS2_I (inode)->ip_blkno); - } - -return ret; +return 0 ; }static void ocfs2_dinode_fill_root_el (struct ocfs2_extent_tree *et)
@@ -5380,13 +5372,13 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb, start_cluster =ocfs2_blocks_to_clusters (osb->sb, start_blk); di = (struct ocfs2_dinode *) tl_bh->b_data; - tl = &di->id2.i_dealloc; -if (!OCFS2_IS_VALID_DINODE (di)) { -OCFS2_RO_ON_INVALID_DINODE (osb->sb, di); - status = -EIO ; - goto bail; - } +/* tl_bh is loaded from ocfs2_truncate_log_init(). It's validated +* by the underlying call to ocfs2_read_inode_block(), so any +* corruption is a code bug */ +BUG_ON (!OCFS2_IS_VALID_DINODE (di)); + + tl = &di->id2.i_dealloc; tl_count =le16_to_cpu (tl->tl_count); mlog_bug_on_msg(tl_count > ocfs2_truncate_recs_per_inode(osb->sb) || tl_count == 0,
@@ -5536,13 +5528,13 @@ int __ocfs2_flush_truncate_log(struct ocfs2_super *osb)BUG_ON (mutex_trylock (&tl_inode->i_mutex)); di = (struct ocfs2_dinode *) tl_bh->b_data; - tl = &di->id2.i_dealloc; -if (!OCFS2_IS_VALID_DINODE (di)) { -OCFS2_RO_ON_INVALID_DINODE (osb->sb, di); - status = -EIO ; - goto out; - } +/* tl_bh is loaded from ocfs2_truncate_log_init(). It's validated +* by the underlying call to ocfs2_read_inode_block(), so any +* corruption is a code bug */ +BUG_ON (!OCFS2_IS_VALID_DINODE (di)); + + tl = &di->id2.i_dealloc; num_to_flush =le16_to_cpu (tl->tl_used);mlog (0 ,"Flush %u records from truncate log #%llu\n" , num_to_flush, (unsigned long long )OCFS2_I (tl_inode)->ip_blkno);
@@ -5697,13 +5689,13 @@ int ocfs2_begin_truncate_log_recovery(struct ocfs2_super *osb, } di = (struct ocfs2_dinode *) tl_bh->b_data; - tl = &di->id2.i_dealloc; -if (!OCFS2_IS_VALID_DINODE (di)) { -OCFS2_RO_ON_INVALID_DINODE (tl_inode->i_sb, di); - status = -EIO ; - goto bail; - } +/* tl_bh is loaded from ocfs2_get_truncate_log_info(). It's +* validated by the underlying call to ocfs2_read_inode_block(), +* so any corruption is a code bug */ +BUG_ON (!OCFS2_IS_VALID_DINODE (di)); + + tl = &di->id2.i_dealloc;if (le16_to_cpu (tl->tl_used)) {mlog (0 ,"We'll have %u logs to recover\n" ,le16_to_cpu (tl->tl_used));
fs/ocfs2/journal.c +5 -12
@@ -587,17 +587,11 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,mlog_entry_void (); fe = (struct ocfs2_dinode *)bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -/* This is called from startup/shutdown which will -* handle the errors in a specific manner, so no need -* to call ocfs2_error() here. */ -mlog (ML_ERROR ,"Journal dinode %llu has invalid " -"signature: %.*s" , - (unsigned long long )le64_to_cpu (fe->i_blkno),7 , - fe->i_signature); - status = -EIO ; - goto out; - } + +/* The journal bh on the osb always comes from ocfs2_journal_init() +* and was validated there inside ocfs2_inode_lock_full(). It's a +* code bug if we mess it up. */ +BUG_ON (!OCFS2_IS_VALID_DINODE (fe)); flags =le32_to_cpu (fe->id1.journal1.ij_flags);if (dirty)
@@ -613,7 +607,6 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,if (status <0 )mlog_errno (status); -out:mlog_exit (status);return status; }
fs/ocfs2/ocfs2.h +0 -8
@@ -443,14 +443,6 @@ static inline int ocfs2_uses_extended_slot_map(struct ocfs2_super *osb)#define OCFS2_IS_VALID_DINODE (ptr) \ (!strcmp((ptr)->i_signature, OCFS2_INODE_SIGNATURE)) -#define OCFS2_RO_ON_INVALID_DINODE (__sb, __di) do { \ - typeof(__di) ____di = (__di); \ - ocfs2_error((__sb), \ - "Dinode # %llu has bad signature %.*s", \ - (unsigned long long)le64_to_cpu((____di)->i_blkno), 7, \ - (____di)->i_signature); \ -} while (0) -#define OCFS2_IS_VALID_EXTENT_BLOCK (ptr) \ (!strcmp((ptr)->h_signature, OCFS2_EXTENT_BLOCK_SIGNATURE))
fs/ocfs2/resize.c +4 -6
@@ -314,6 +314,10 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters) fe = (struct ocfs2_dinode *)main_bm_bh->b_data; +/* main_bm_bh is validated by inode read inside ocfs2_inode_lock(), +* so any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_DINODE (fe)); +if (le16_to_cpu (fe->id2.i_chain.cl_cpg) !=ocfs2_group_bitmap_size (osb->sb) *8 ) { mlog(ML_ERROR, "The disk is too old and small. "
@@ -322,12 +326,6 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters) goto out_unlock; } -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (main_bm_inode->i_sb, fe); - ret = -EIO ; - goto out_unlock; - } - first_new_cluster =le32_to_cpu (fe->i_clusters); lgd_blkno =ocfs2_which_cluster_group (main_bm_inode, first_new_cluster -1 );
fs/ocfs2/suballoc.c +16 -20
@@ -441,11 +441,11 @@ static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb, ac->ac_alloc_slot = slot; fe = (struct ocfs2_dinode *) bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (alloc_inode->i_sb, fe); - status = -EIO ; - goto bail; - } + +/* The bh was validated by the inode read inside +* ocfs2_inode_lock(). Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_DINODE (fe)); +if (!(fe->i_flags &cpu_to_le32 (OCFS2_CHAIN_FL ))) {ocfs2_error (alloc_inode->i_sb,"Invalid chain allocator %llu" , (unsigned long long )le64_to_cpu (fe->i_blkno));
@@ -931,11 +931,6 @@ static int ocfs2_relink_block_group(handle_t *handle,struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) bg_bh->b_data;struct ocfs2_group_desc *prev_bg = (struct ocfs2_group_desc *) prev_bg_bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (alloc_inode->i_sb, fe); - status = -EIO ; - goto out; - }if (!OCFS2_IS_VALID_GROUP_DESC (bg)) {OCFS2_RO_ON_INVALID_GROUP_DESC (alloc_inode->i_sb, bg); status = -EIO ;
@@ -1392,11 +1387,11 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,BUG_ON (!ac->ac_bh); fe = (struct ocfs2_dinode *) ac->ac_bh->b_data; -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (osb->sb, fe); - status = -EIO ; - goto bail; - } + +/* The bh was validated by the inode read during +* ocfs2_reserve_suballoc_bits(). Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_DINODE (fe)); +if (le32_to_cpu (fe->id1.bitmap1.i_used) >=le32_to_cpu (fe->id1.bitmap1.i_total)) { ocfs2_error(osb->sb, "Chain allocator dinode %llu has %u used "
@@ -1782,11 +1777,12 @@ int ocfs2_free_suballoc_bits(handle_t *handle,mlog_entry_void (); -if (!OCFS2_IS_VALID_DINODE (fe)) { -OCFS2_RO_ON_INVALID_DINODE (alloc_inode->i_sb, fe); - status = -EIO ; - goto bail; - } +/* The alloc_bh comes from ocfs2_free_dinode() or +* ocfs2_free_clusters(). The callers have all locked the +* allocator and gotten alloc_bh from the lock call. This +* validates the dinode buffer. Any corruption that has happended +* is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_DINODE (fe));BUG_ON ((count + start_bit) >ocfs2_bits_per_group (cl)); mlog(0, "%llu: freeing %u bits from group %llu, starting at %u\n",
Signature · 1 line
1.5.6.5
PATCH 3/11 ocfs2: Consolidate validation of group descriptors.
In reply to Joel BeckerCurrently the validation of group descriptors is directly duplicated so that one version can error the filesystem and the other (resize) can just report the problem. Consolidate to one function that takes a boolean. Wrap that function with the old call for the old users.
This is in preparation for lifting the read+validate step into a single function.
--- fs/ocfs2/resize.c | 40 +++++---------------------- fs/ocfs2/suballoc.c | 74 +++++++++++++++++++++++++++++--------------------- fs/ocfs2/suballoc.h | 20 +++++++++++-- 3 files changed, 68 insertions(+), 66 deletions(-)
fs/ocfs2/resize.c +8 -32
@@ -396,41 +396,16 @@ static int ocfs2_check_new_group(struct inode *inode, struct buffer_head *group_bh) {int ret; -struct ocfs2_group_desc *gd; +struct ocfs2_group_desc *gd = + (struct ocfs2_group_desc *)group_bh->b_data;u16 cl_bpc =le16_to_cpu (di->id2.i_chain.cl_bpc); -unsigned int max_bits =le16_to_cpu (di->id2.i_chain.cl_cpg) * -le16_to_cpu (di->id2.i_chain.cl_bpc); + ret =ocfs2_validate_group_descriptor (inode->i_sb, di, gd,1 ); +if (ret) + goto out; - gd = (struct ocfs2_group_desc *)group_bh->b_data; - - ret = -EIO ; -if (!OCFS2_IS_VALID_GROUP_DESC (gd)) -mlog (ML_ERROR ,"Group descriptor # %llu isn't valid.\n" , - (unsigned long long )le64_to_cpu (gd->bg_blkno)); -else if (di->i_blkno != gd->bg_parent_dinode) -mlog (ML_ERROR ,"Group descriptor # %llu has bad parent " -"pointer (%llu, expected %llu)\n" , - (unsigned long long )le64_to_cpu (gd->bg_blkno), - (unsigned long long )le64_to_cpu (gd->bg_parent_dinode), - (unsigned long long )le64_to_cpu (di->i_blkno)); -else if (le16_to_cpu (gd->bg_bits) > max_bits) -mlog (ML_ERROR ,"Group descriptor # %llu has bit count of %u\n" , - (unsigned long long )le64_to_cpu (gd->bg_blkno), -le16_to_cpu (gd->bg_bits)); -else if (le16_to_cpu (gd->bg_free_bits_count) >le16_to_cpu (gd->bg_bits)) -mlog (ML_ERROR ,"Group descriptor # %llu has bit count %u but " -"claims that %u are free\n" , - (unsigned long long )le64_to_cpu (gd->bg_blkno), -le16_to_cpu (gd->bg_bits), -le16_to_cpu (gd->bg_free_bits_count)); -else if (le16_to_cpu (gd->bg_bits) > (8 *le16_to_cpu (gd->bg_size))) -mlog (ML_ERROR ,"Group descriptor # %llu has bit count %u but " -"max bitmap bits of %u\n" , - (unsigned long long )le64_to_cpu (gd->bg_blkno), -le16_to_cpu (gd->bg_bits), -8 *le16_to_cpu (gd->bg_size)); -else if (le16_to_cpu (gd->bg_chain) != input->chain) + ret = -EINVAL ; +if (le16_to_cpu (gd->bg_chain) != input->chain) mlog(ML_ERROR, "Group descriptor # %llu has bad chain %u " "while input has %u set.\n", (unsigned long long)le64_to_cpu(gd->bg_blkno),
@@ -449,6 +424,7 @@ static int ocfs2_check_new_group(struct inode *inode,else ret =0 ; +out:return ret; }
fs/ocfs2/suballoc.c +43 -31
102 lines not on this page · raw message
fs/ocfs2/suballoc.h +17 -3
@@ -165,9 +165,23 @@ void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac);u64 ocfs2_which_cluster_group (struct inode *inode,u32 cluster);/* somewhat more expensive than our other checks, so use sparingly. */ -int ocfs2_check_group_descriptor (struct super_block *sb, -struct ocfs2_dinode *di, -struct ocfs2_group_desc *gd); +/* +* By default, ocfs2_validate_group_descriptor() calls ocfs2_error() when it +* finds a problem. A caller that wants to check a group descriptor +* without going readonly passes a nonzero clean_error. This is only +* resize, really. +*/ +int ocfs2_validate_group_descriptor (struct super_block *sb, +struct ocfs2_dinode *di, +struct ocfs2_group_desc *gd, +int clean_error); +static inline int ocfs2_check_group_descriptor (struct super_block *sb, +struct ocfs2_dinode *di, +struct ocfs2_group_desc *gd) +{ +return ocfs2_validate_group_descriptor (sb, di, gd,0 ); +} + int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac,
Signature · 1 line
1.5.6.5
PATCH 4/11 ocfs2: Wrap group descriptor reads in a dedicated function.
In reply to Joel BeckerWe have a clean call for validating group descriptors, but every place that wants the always does a read_block()+validate() call pair. Create a toplevel ocfs2_read_group_descriptor() that does the right thing. This allows us to leverage the single call point later for fancier handling. We also add validation of gd->bg_generation against the superblock and gd->bg_blkno against the block we thought we read.
--- fs/ocfs2/resize.c | 12 +---- fs/ocfs2/suballoc.c | 108 ++++++++++++++++++++++++++++++-------------------- fs/ocfs2/suballoc.h | 19 +++++---- 3 files changed, 78 insertions(+), 61 deletions(-)
fs/ocfs2/resize.c +3 -9
@@ -330,20 +330,14 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters) lgd_blkno =ocfs2_which_cluster_group (main_bm_inode, first_new_cluster -1 ); - ret =ocfs2_read_block (main_bm_inode, lgd_blkno, &group_bh); + ret =ocfs2_read_group_descriptor (main_bm_inode, fe, lgd_blkno, + &group_bh);if (ret <0 ) {mlog_errno (ret); goto out_unlock; } - group = (struct ocfs2_group_desc *)group_bh->b_data; - ret =ocfs2_check_group_descriptor (inode->i_sb, fe, group); -if (ret) { -mlog_errno (ret); - goto out_unlock; - } - cl_bpc =le16_to_cpu (fe->id2.i_chain.cl_bpc);if (le16_to_cpu (group->bg_bits) / cl_bpc + new_clusters >le16_to_cpu (fe->id2.i_chain.cl_cpg)) {
@@ -400,7 +394,7 @@ static int ocfs2_check_new_group(struct inode *inode, (struct ocfs2_group_desc *)group_bh->b_data;u16 cl_bpc =le16_to_cpu (di->id2.i_chain.cl_bpc); - ret =ocfs2_validate_group_descriptor (inode->i_sb, di, gd,1 ); + ret =ocfs2_validate_group_descriptor (inode->i_sb, di, group_bh,1 );if (ret) goto out;
fs/ocfs2/suballoc.c +65 -43
209 lines not on this page · raw message
fs/ocfs2/suballoc.h +10 -9
@@ -164,23 +164,24 @@ void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac);* and return that block offset. */ u64 ocfs2_which_cluster_group (struct inode *inode,u32 cluster); -/* somewhat more expensive than our other checks, so use sparingly. */ /* * By default, ocfs2_validate_group_descriptor() calls ocfs2_error() when it * finds a problem. A caller that wants to check a group descriptor * without going readonly passes a nonzero clean_error. This is only -* resize, really. +* resize, really. Everyone else should be using +* ocfs2_read_group_descriptor(). */ int ocfs2_validate_group_descriptor (struct super_block *sb,struct ocfs2_dinode *di, -struct ocfs2_group_desc *gd, +struct buffer_head *bh,int clean_error); -static inline int ocfs2_check_group_descriptor (struct super_block *sb, -struct ocfs2_dinode *di, -struct ocfs2_group_desc *gd) -{ -return ocfs2_validate_group_descriptor (sb, di, gd,0 ); -} +/* +* Read a group descriptor block into *bh. If *bh is NULL, a bh will be +* allocated. This is a cached read. The descriptor will be validated with +* ocfs2_validate_group_descriptor(). +*/ +int ocfs2_read_group_descriptor (struct inode *inode,struct ocfs2_dinode *di, +u64 gd_blkno,struct buffer_head **bh); int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et, u32 clusters_to_add, u32 extents_to_split,
Signature · 1 line
1.5.6.5
PATCH 5/11 ocfs2: Morph the haphazard OCFS2_IS_VALID_GROUP_DESC() checks.
In reply to Joel BeckerRandom places in the code would check a group descriptor bh to see if it was valid. The previous commit unified descriptor block reads, validating all block reads in the same place. Thus, these checks are no longer necessary. Rather than eliminate them, however, we change them to BUG_ON() checks. This ensures the assumptions remain true. All of the code paths to these checks have been audited to ensure they come from a validated descriptor read.
--- fs/ocfs2/ocfs2.h | 7 ------- fs/ocfs2/suballoc.c | 39 ++++++++++++++------------------------- 2 files changed, 14 insertions(+), 32 deletions(-)
fs/ocfs2/ocfs2.h +0 -7
@@ -457,13 +457,6 @@ static inline int ocfs2_uses_extended_slot_map(struct ocfs2_super *osb)#define OCFS2_IS_VALID_GROUP_DESC (ptr) \ (!strcmp((ptr)->bg_signature, OCFS2_GROUP_DESC_SIGNATURE)) -#define OCFS2_RO_ON_INVALID_GROUP_DESC (__sb, __gd) do { \ - typeof(__gd) ____gd = (__gd); \ - ocfs2_error((__sb), \ - "Group Descriptor # %llu has bad signature %.*s", \ - (unsigned long long)le64_to_cpu((____gd)->bg_blkno), 7, \ - (____gd)->bg_signature); \ -} while (0)#define OCFS2_IS_VALID_XATTR_BLOCK (ptr) \ (!strcmp((ptr)->xb_signature, OCFS2_XATTR_BLOCK_SIGNATURE))
fs/ocfs2/suballoc.c +14 -25
@@ -842,10 +842,9 @@ static int ocfs2_block_group_find_clear_bits(struct ocfs2_super *osb,int offset, start, found, status =0 ;struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) bg_bh->b_data; -if (!OCFS2_IS_VALID_GROUP_DESC (bg)) { -OCFS2_RO_ON_INVALID_GROUP_DESC (osb->sb, bg); -return -EIO ; - } +/* Callers got this descriptor from +* ocfs2_read_group_descriptor(). Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_GROUP_DESC (bg)); found = start = best_offset = best_size =0 ; bitmap = bg->bg_bitmap;
@@ -910,11 +909,9 @@ static inline int ocfs2_block_group_set_bits(handle_t *handle,mlog_entry_void (); -if (!OCFS2_IS_VALID_GROUP_DESC (bg)) { -OCFS2_RO_ON_INVALID_GROUP_DESC (alloc_inode->i_sb, bg); - status = -EIO ; - goto bail; - } +/* All callers get the descriptor via +* ocfs2_read_group_descriptor(). Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_GROUP_DESC (bg));BUG_ON (le16_to_cpu (bg->bg_free_bits_count) < num_bits); mlog(0, "block_group_set_bits: off = %u, num = %u\n", bit_off,
@@ -983,16 +980,10 @@ static int ocfs2_relink_block_group(handle_t *handle,struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) bg_bh->b_data;struct ocfs2_group_desc *prev_bg = (struct ocfs2_group_desc *) prev_bg_bh->b_data; -if (!OCFS2_IS_VALID_GROUP_DESC (bg)) { -OCFS2_RO_ON_INVALID_GROUP_DESC (alloc_inode->i_sb, bg); - status = -EIO ; - goto out; - } -if (!OCFS2_IS_VALID_GROUP_DESC (prev_bg)) { -OCFS2_RO_ON_INVALID_GROUP_DESC (alloc_inode->i_sb, prev_bg); - status = -EIO ; - goto out; - } +/* The caller got these descriptors from +* ocfs2_read_group_descriptor(). Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_GROUP_DESC (bg)); +BUG_ON (!OCFS2_IS_VALID_GROUP_DESC (prev_bg)); mlog(0, "Suballoc %llu, chain %u, move group %llu to top, prev = %llu\n", (unsigned long long)le64_to_cpu(fe->i_blkno), chain,
@@ -1055,7 +1046,7 @@ out_rollback: bg->bg_next_group =cpu_to_le64 (bg_ptr); prev_bg->bg_next_group =cpu_to_le64 (prev_bg_ptr); } -out: +mlog_exit (status);return status; }
@@ -1758,11 +1749,9 @@ static inline int ocfs2_block_group_clear_bits(handle_t *handle,mlog_entry_void (); -if (!OCFS2_IS_VALID_GROUP_DESC (bg)) { -OCFS2_RO_ON_INVALID_GROUP_DESC (alloc_inode->i_sb, bg); - status = -EIO ; - goto bail; - } +/* The caller got this descriptor from +* ocfs2_read_group_descriptor(). Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_GROUP_DESC (bg));mlog (0 ,"off = %u, num = %u\n" , bit_off, num_bits);
Signature · 1 line
1.5.6.5
PATCH 6/11 ocfs2: Wrap extent block reads in a dedicated function.
In reply to Joel BeckerWe weren't consistently checking extent blocks after we read them. Most places checked the signature, but none checked h_blkno or h_fs_signature. Create a toplevel ocfs2_read_extent_block() that does the read and the validation.
--- fs/ocfs2/alloc.c | 151 ++++++++++++++++++++++++++++++++----------------- fs/ocfs2/alloc.h | 8 +++ fs/ocfs2/extent_map.c | 23 ++------ fs/ocfs2/ocfs2.h | 8 --- 4 files changed, 111 insertions(+), 79 deletions(-)
fs/ocfs2/alloc.c +98 -53
@@ -678,6 +678,66 @@ struct ocfs2_merge_ctxt {int c_split_covers_rec; }; +static int ocfs2_validate_extent_block (struct super_block *sb, +struct buffer_head *bh) +{ +struct ocfs2_extent_block *eb = + (struct ocfs2_extent_block *)bh->b_data; + +if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { +ocfs2_error (sb, +"Extent block #%llu has bad signature %.*s" , + (unsigned long long )bh->b_blocknr,7 , + eb->h_signature); +return -EINVAL ; + } + +if (le64_to_cpu (eb->h_blkno) != bh->b_blocknr) { +ocfs2_error (sb, +"Extent block #%llu has an invalid h_blkno " +"of %llu" , + (unsigned long long )bh->b_blocknr, + (unsigned long long )le64_to_cpu (eb->h_blkno)); +return -EINVAL ; + } + +if (le32_to_cpu (eb->h_fs_generation) !=OCFS2_SB (sb)->fs_generation) { +ocfs2_error (sb, +"Extent block #%llu has an invalid " +"h_fs_generation of #%u" , + (unsigned long long )bh->b_blocknr, +le32_to_cpu (eb->h_fs_generation)); +return -EINVAL ; + } + +return 0 ; +} + +int ocfs2_read_extent_block (struct inode *inode,u64 eb_blkno, +struct buffer_head **bh) +{ +int rc; +struct buffer_head *tmp = *bh; + + rc =ocfs2_read_block (inode, eb_blkno, &tmp); +if (rc) + goto out; + + rc =ocfs2_validate_extent_block (inode->i_sb, tmp); +if (rc) { +brelse (tmp); + goto out; + } + +/* If ocfs2_read_block() got us a new bh, pass it up. */ +if (!*bh) + *bh = tmp; + +out: +return rc; +} + +/* * How many free extents have we got before we need more meta data? */
@@ -697,8 +757,7 @@ int ocfs2_num_free_extents(struct ocfs2_super *osb, last_eb_blk =ocfs2_et_get_last_eb_blk (et);if (last_eb_blk) { - retval =ocfs2_read_block (inode, last_eb_blk, - &eb_bh); + retval =ocfs2_read_extent_block (inode, last_eb_blk, &eb_bh);if (retval <0 ) {mlog_errno (retval); goto bail;
@@ -900,11 +959,8 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,for (i =0 ; i < new_blocks; i++) { bh = new_eb_bhs[i]; eb = (struct ocfs2_extent_block *) bh->b_data; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - status = -EIO ; - goto bail; - } +/* ocfs2_create_new_meta_bhs() should create it right! */ +BUG_ON (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)); eb_el = &eb->h_list; status = ocfs2_journal_access(handle, inode, bh,
@@ -1044,11 +1100,8 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, } eb = (struct ocfs2_extent_block *) new_eb_bh->b_data; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - status = -EIO ; - goto bail; - } +/* ocfs2_create_new_meta_bhs() should create it right! */ +BUG_ON (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)); eb_el = &eb->h_list; root_el = et->et_root_el;
@@ -1168,18 +1221,13 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb,brelse (bh); bh =NULL ; - status =ocfs2_read_block (inode, blkno, &bh); + status =ocfs2_read_extent_block (inode, blkno, &bh);if (status <0 ) {mlog_errno (status); goto bail; } eb = (struct ocfs2_extent_block *) bh->b_data; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - status = -EIO ; - goto bail; - } el = &eb->h_list; if (le16_to_cpu(el->l_next_free_rec) <
@@ -1532,7 +1580,7 @@ static int __ocfs2_find_path(struct inode *inode,brelse (bh); bh =NULL ; - ret =ocfs2_read_block (inode, blkno, &bh); + ret =ocfs2_read_extent_block (inode, blkno, &bh);if (ret) {mlog_errno (ret); goto out;
@@ -1540,11 +1588,6 @@ static int __ocfs2_find_path(struct inode *inode, eb = (struct ocfs2_extent_block *) bh->b_data; el = &eb->h_list; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - ret = -EIO ; - goto out; - }if (le16_to_cpu (el->l_next_free_rec) >le16_to_cpu (el->l_count)) {
@@ -4089,8 +4132,15 @@ ocfs2_figure_merge_contig_type(struct inode *inode, struct ocfs2_path *path, le16_to_cpu(new_el->l_count)) { bh =path_leaf_bh (left_path); eb = (struct ocfs2_extent_block *)bh->b_data; -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, - eb); +ocfs2_error (inode->i_sb, +"Extent block #%llu has an " +"invalid l_next_free_rec of " +"%d. It should have " +"matched the l_count of %d" , + (unsigned long long )le64_to_cpu (eb->h_blkno), +le16_to_cpu (new_el->l_next_free_rec), +le16_to_cpu (new_el->l_count)); + status = -EINVAL ; goto out; } rec = &new_el->l_recs[
@@ -4139,8 +4189,12 @@ ocfs2_figure_merge_contig_type(struct inode *inode, struct ocfs2_path *path,if (le16_to_cpu (new_el->l_next_free_rec) <=1 ) { bh =path_leaf_bh (right_path); eb = (struct ocfs2_extent_block *)bh->b_data; -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, - eb); +ocfs2_error (inode->i_sb, +"Extent block #%llu has an " +"invalid l_next_free_rec of %d" , + (unsigned long long )le64_to_cpu (eb->h_blkno), +le16_to_cpu (new_el->l_next_free_rec)); + status = -EINVAL ; goto out; } rec = &new_el->l_recs[1 ];
@@ -4286,7 +4340,9 @@ static int ocfs2_figure_insert_type(struct inode *inode,* ocfs2_figure_insert_type() and ocfs2_add_branch() * may want it later. */ - ret =ocfs2_read_block (inode,ocfs2_et_get_last_eb_blk (et), &bh); + ret =ocfs2_read_extent_block (inode, +ocfs2_et_get_last_eb_blk (et), + &bh);if (ret) {mlog_exit (ret); goto out;
@@ -4752,20 +4808,15 @@ static int __ocfs2_mark_extent_written(struct inode *inode,if (path->p_tree_depth) {struct ocfs2_extent_block *eb; - ret =ocfs2_read_block (inode,ocfs2_et_get_last_eb_blk (et), - &last_eb_bh); + ret =ocfs2_read_extent_block (inode, +ocfs2_et_get_last_eb_blk (et), + &last_eb_bh);if (ret) {mlog_exit (ret); goto out; } eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - ret = -EROFS ; - goto out; - } - rightmost_el = &eb->h_list; }else rightmost_el =path_root_el (path);
@@ -4910,8 +4961,9 @@ static int ocfs2_split_tree(struct inode *inode, struct ocfs2_extent_tree *et, depth = path->p_tree_depth;if (depth >0 ) { - ret =ocfs2_read_block (inode,ocfs2_et_get_last_eb_blk (et), - &last_eb_bh); + ret =ocfs2_read_extent_block (inode, +ocfs2_et_get_last_eb_blk (et), + &last_eb_bh);if (ret <0 ) {mlog_errno (ret); goto out;
@@ -6231,11 +6283,10 @@ static int ocfs2_find_new_last_ext_blk(struct inode *inode, eb = (struct ocfs2_extent_block *) bh->b_data; el = &eb->h_list; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - ret = -EROFS ; - goto out; - } + +/* ocfs2_find_leaf() gets the eb from ocfs2_read_extent_block(). +* Any corruption is a code bug. */ +BUG_ON (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)); *new_last_eb = bh;get_bh (*new_last_eb);
@@ -7140,20 +7191,14 @@ int ocfs2_prepare_truncate(struct ocfs2_super *osb,ocfs2_init_dealloc_ctxt (&(*tc)->tc_dealloc);if (fe->id2.i_list.l_tree_depth) { - status =ocfs2_read_block (inode,le64_to_cpu (fe->i_last_eb_blk), - &last_eb_bh); + status =ocfs2_read_extent_block (inode, +le64_to_cpu (fe->i_last_eb_blk), + &last_eb_bh);if (status <0 ) {mlog_errno (status); goto bail; } eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - -brelse (last_eb_bh); - status = -EIO ; - goto bail; - } } (*tc)->tc_last_eb_bh = last_eb_bh;
fs/ocfs2/alloc.h +8 -0
@@ -73,6 +73,14 @@ void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et,struct buffer_head *bh, struct ocfs2_xattr_value_root *xv); +/* +* Read an extent block into *bh. If *bh is NULL, a bh will be +* allocated. This is a cached read. The extent block will be validated +* with ocfs2_validate_extent_block(). +*/ +int ocfs2_read_extent_block (struct inode *inode,u64 eb_blkno, +struct buffer_head **bh); +struct ocfs2_alloc_context ; int ocfs2_insert_extent(struct ocfs2_super *osb, handle_t *handle,
fs/ocfs2/extent_map.c +5 -18
@@ -293,7 +293,7 @@ static int ocfs2_last_eb_is_empty(struct inode *inode,struct ocfs2_extent_block *eb;struct ocfs2_extent_list *el; - ret =ocfs2_read_block (inode, last_eb_blk, &eb_bh); + ret =ocfs2_read_extent_block (inode, last_eb_blk, &eb_bh);if (ret) {mlog_errno (ret); goto out;
@@ -302,12 +302,6 @@ static int ocfs2_last_eb_is_empty(struct inode *inode, eb = (struct ocfs2_extent_block *) eb_bh->b_data; el = &eb->h_list; -if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { - ret = -EROFS ; -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, eb); - goto out; - } -if (el->l_tree_depth) {ocfs2_error (inode->i_sb, "Inode %lu has non zero tree depth in "
@@ -381,23 +375,16 @@ static int ocfs2_figure_hole_clusters(struct inode *inode,if (le64_to_cpu (eb->h_next_leaf_blk) ==0ULL ) goto no_more_extents; - ret =ocfs2_read_block (inode, -le64_to_cpu (eb->h_next_leaf_blk), - &next_eb_bh); + ret =ocfs2_read_extent_block (inode, +le64_to_cpu (eb->h_next_leaf_blk), + &next_eb_bh);if (ret) {mlog_errno (ret); goto out; } - next_eb = (struct ocfs2_extent_block *)next_eb_bh->b_data; - -if (!OCFS2_IS_VALID_EXTENT_BLOCK (next_eb)) { - ret = -EROFS ; -OCFS2_RO_ON_INVALID_EXTENT_BLOCK (inode->i_sb, next_eb); - goto out; - } + next_eb = (struct ocfs2_extent_block *)next_eb_bh->b_data; el = &next_eb->h_list; - i =ocfs2_search_for_hole_index (el, v_cluster); }
fs/ocfs2/ocfs2.h +0 -8
@@ -446,14 +446,6 @@ static inline int ocfs2_uses_extended_slot_map(struct ocfs2_super *osb)#define OCFS2_IS_VALID_EXTENT_BLOCK (ptr) \ (!strcmp((ptr)->h_signature, OCFS2_EXTENT_BLOCK_SIGNATURE)) -#define OCFS2_RO_ON_INVALID_EXTENT_BLOCK (__sb, __eb) do { \ - typeof(__eb) ____eb = (__eb); \ - ocfs2_error((__sb), \ - "Extent Block # %llu has bad signature %.*s", \ - (unsigned long long)le64_to_cpu((____eb)->h_blkno), 7, \ - (____eb)->h_signature); \ -} while (0) -#define OCFS2_IS_VALID_GROUP_DESC (ptr) \ (!strcmp((ptr)->bg_signature, OCFS2_GROUP_DESC_SIGNATURE))
Signature · 1 line
1.5.6.5
PATCH 7/11 ocfs2: Wrap dirblock reads in a dedicated function.
In reply to Joel BeckerWe have ocfs2_bread() as a vestige of the original ext-based dir code. It's only used by directories, though. Turn it into ocfs2_read_dir_block(), with a prototype matching the other metadata read functions. It's set up to validate dirblocks when the time comes.
--- fs/ocfs2/dir.c | 150 +++++++++++++++++++++++++++++++++----------------------- 1 files changed, 88 insertions(+), 62 deletions(-)
fs/ocfs2/dir.c +88 -62
@@ -82,49 +82,6 @@ static int ocfs2_do_extend_dir(struct super_block *sb,struct ocfs2_alloc_context *meta_ac, struct buffer_head **new_bh); -static struct buffer_head *ocfs2_bread (struct inode *inode, -int block,int *err,int reada) -{ -struct buffer_head *bh =NULL ; -int tmperr; -u64 p_blkno; -int readflags =0 ; - -if (reada) - readflags |=OCFS2_BH_READAHEAD ; - -if (((u64 )block << inode->i_sb->s_blocksize_bits) >= -i_size_read (inode)) { -BUG_ON (!reada); -return NULL ; - } - -down_read (&OCFS2_I (inode)->ip_alloc_sem); - tmperr =ocfs2_extent_map_get_blocks (inode, block, &p_blkno,NULL , -NULL ); -up_read (&OCFS2_I (inode)->ip_alloc_sem); -if (tmperr <0 ) { -mlog_errno (tmperr); - goto fail; - } - - tmperr =ocfs2_read_blocks (inode, p_blkno,1 , &bh, readflags); -if (tmperr <0 ) - goto fail; - - tmperr =0 ; - - *err =0 ; -return bh; - -fail: -brelse (bh); - bh =NULL ; - - *err = -EIO ; -return NULL ; -} -/* * bh passed here can be an inode block or a dir data block, depending * on the inode inline data flag.
@@ -250,6 +207,76 @@ out:return NULL ; } +static int ocfs2_validate_dir_block (struct super_block *sb, +struct buffer_head *bh) +{ +/* +* Nothing yet. We don't validate dirents here, that's handled +* in-place when the code walks them. +*/ + +return 0 ; +} + +/* +* This function forces all errors to -EIO for consistency with its +* predecessor, ocfs2_bread(). We haven't audited what returning the +* real error codes would do to callers. We log the real codes with +* mlog_errno() before we squash them. +*/ +static int ocfs2_read_dir_block (struct inode *inode,u64 v_block, +struct buffer_head **bh,int flags) +{ +int rc =0 ; +struct buffer_head *tmp = *bh; +u64 p_blkno; + +if (((u64 )v_block << inode->i_sb->s_blocksize_bits) >= +i_size_read (inode)) { +BUG_ON (!(flags &OCFS2_BH_READAHEAD )); + goto out; + } + +down_read (&OCFS2_I (inode)->ip_alloc_sem); + rc =ocfs2_extent_map_get_blocks (inode, v_block, &p_blkno,NULL , +NULL ); +up_read (&OCFS2_I (inode)->ip_alloc_sem); +if (rc) { +mlog_errno (rc); + goto out; + } + +if (!p_blkno) { + rc = -EIO ; +mlog (ML_ERROR , +"Directory #%llu contains a hole at offset %llu\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + (unsigned long long )v_block << inode->i_sb->s_blocksize_bits); + goto out; + } + + rc =ocfs2_read_blocks (inode, p_blkno,1 , &tmp, flags); +if (rc) { +mlog_errno (rc); + goto out; + } + +if (!(flags &OCFS2_BH_READAHEAD )) { + rc =ocfs2_validate_dir_block (inode->i_sb, tmp); +if (rc) { +brelse (tmp); + goto out; + } + } + +/* If ocfs2_read_blocks() got us a new bh, pass it up. */ +if (!*bh) + *bh = tmp; + +out: +return rc ? -EIO :0 ; +} +static struct buffer_head *ocfs2_find_entry_el (const char *name,int namelen,struct inode *dir,struct ocfs2_dir_entry **res_dir)
@@ -296,15 +323,17 @@ restart: } num++; - bh =ocfs2_bread (dir, b++, &err,1 ); + bh =NULL ; + err =ocfs2_read_dir_block (dir, b++, &bh, +OCFS2_BH_READAHEAD ); bh_use[ra_max] = bh; } }if ((bh = bh_use[ra_ptr++]) ==NULL ) goto next; -if (ocfs2_read_block (dir, block, &bh)) { +if (ocfs2_read_dir_block (dir, block, &bh,0 )) {/* read error, skip block & hope for the best. -* ocfs2_read_block() has released the bh. */ +* ocfs2_read_dir_block() has released the bh. */ ocfs2_error(dir->i_sb, "reading directory %llu, " "offset %lu\n", (unsigned long long)OCFS2_I(dir)->ip_blkno,
@@ -724,7 +753,6 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode,int i, stored;struct buffer_head * bh, * tmp;struct ocfs2_dir_entry * de; -int err;struct super_block * sb = inode->i_sb;unsigned int ra_sectors =16 ;
@@ -735,12 +763,8 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode,while (!error && !stored && *f_pos <i_size_read (inode)) { blk = (*f_pos) >> sb->s_blocksize_bits; - bh =ocfs2_bread (inode, blk, &err,0 ); -if (!bh) { -mlog (ML_ERROR , -"directory #%llu contains a hole at offset %lld\n" , - (unsigned long long )OCFS2_I (inode)->ip_blkno, - *f_pos); +if (ocfs2_read_dir_block (inode, blk, &bh,0 )) { +/* Skip the corrupt dirblock and keep trying */ *f_pos += sb->s_blocksize - offset;continue ; }
@@ -754,8 +778,10 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode, || (((last_ra_blk - blk) << 9) <= (ra_sectors / 2))) {for (i = ra_sectors >> (sb->s_blocksize_bits -9 ); i >0 ; i--) { - tmp =ocfs2_bread (inode, ++blk, &err,1 ); -brelse (tmp); + tmp =NULL ; +if (!ocfs2_read_dir_block (inode, ++blk, &tmp, +OCFS2_BH_READAHEAD )) +brelse (tmp); } last_ra_blk = blk; ra_sectors =8 ;
@@ -828,6 +854,7 @@ revalidate: } offset =0 ;brelse (bh); + bh =NULL ; } stored =0 ;
@@ -1680,8 +1707,8 @@ static int ocfs2_find_dir_space_el(struct inode *dir, const char *name,struct super_block *sb = dir->i_sb;int status; - bh =ocfs2_bread (dir,0 , &status,0 ); -if (!bh) { + status =ocfs2_read_dir_block (dir,0 , &bh,0 ); +if (status) {mlog_errno (status); goto bail; }
@@ -1702,11 +1729,10 @@ static int ocfs2_find_dir_space_el(struct inode *dir, const char *name, status = -ENOSPC ; goto bail; } - bh =ocfs2_bread (dir, - offset >> sb->s_blocksize_bits, - &status, -0 ); -if (!bh) { + status =ocfs2_read_dir_block (dir, + offset >> sb->s_blocksize_bits, + &bh,0 ); +if (status) {mlog_errno (status); goto bail; }
Signature · 1 line
1.5.6.5
PATCH 8/11 ocfs2: Wrap xattr block reads in a dedicated function
In reply to Joel BeckerWe weren't consistently checking extent blocks after we read them. Most places checked the signature, but none checked xb_blkno or xb_fs_signature. Create a toplevel ocfs2_read_xattr_block() that does the read and the validation.
--- fs/ocfs2/xattr.c | 94 ++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 70 insertions(+), 24 deletions(-)
fs/ocfs2/xattr.c +70 -24
@@ -294,6 +294,65 @@ static void ocfs2_xattr_bucket_copy_data(struct ocfs2_xattr_bucket *dest, } } +static int ocfs2_validate_xattr_block (struct super_block *sb, +struct buffer_head *bh) +{ +struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)bh->b_data; + +mlog (0 ,"Validating xattr block %llu\n" , + (unsigned long long )bh->b_blocknr); + +if (!OCFS2_IS_VALID_XATTR_BLOCK (xb)) { +ocfs2_error (sb, +"Extended attribute block #%llu has bad " +"signature %.*s" , + (unsigned long long )bh->b_blocknr,7 , + xb->xb_signature); +return -EINVAL ; + } + +if (le64_to_cpu (xb->xb_blkno) != bh->b_blocknr) { +ocfs2_error (sb, +"Extended attribute block #%llu has an " +"invalid xb_blkno of %llu" , + (unsigned long long )bh->b_blocknr, + (unsigned long long )le64_to_cpu (xb->xb_blkno)); +return -EINVAL ; + } + +if (le32_to_cpu (xb->xb_fs_generation) !=OCFS2_SB (sb)->fs_generation) { +ocfs2_error (sb, +"Extended attribute block #%llu has an invalid " +"xb_fs_generation of #%u" , + (unsigned long long )bh->b_blocknr, +le32_to_cpu (xb->xb_fs_generation)); +return -EINVAL ; + } + +return 0 ; +} + +static int ocfs2_read_xattr_block (struct inode *inode,u64 xb_blkno, +struct buffer_head **bh) +{ +int rc; +struct buffer_head *tmp = *bh; + + rc =ocfs2_read_block (inode, xb_blkno, &tmp); +if (!rc) { + rc =ocfs2_validate_xattr_block (inode->i_sb, tmp); +if (rc) +brelse (tmp); + } + +/* If ocfs2_read_block() got us a new bh, pass it up. */ +if (!rc && !*bh) + *bh = tmp; + +return rc; +} +static inline const char *ocfs2_xattr_prefix (int name_index) {struct xattr_handler *handler =NULL ;
@@ -598,18 +657,14 @@ static int ocfs2_xattr_block_list(struct inode *inode,if (!di->i_xattr_loc)return ret; - ret =ocfs2_read_block (inode,le64_to_cpu (di->i_xattr_loc), &blk_bh); + ret =ocfs2_read_xattr_block (inode,le64_to_cpu (di->i_xattr_loc), + &blk_bh);if (ret <0 ) {mlog_errno (ret);return ret; } xb = (struct ocfs2_xattr_block *)blk_bh->b_data; -if (!OCFS2_IS_VALID_XATTR_BLOCK (xb)) { - ret = -EIO ; - goto cleanup; - } -if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) {struct ocfs2_xattr_header *header = &xb->xb_attrs.xb_header; ret = ocfs2_xattr_list_entries(inode, header,
@@ -619,7 +674,7 @@ static int ocfs2_xattr_block_list(struct inode *inode, ret =ocfs2_xattr_tree_list_index_block (inode, xt, buffer, buffer_size); } -cleanup: +brelse (blk_bh);return ret;
@@ -1536,24 +1591,19 @@ static int ocfs2_xattr_free_block(struct inode *inode,u64 blk, bg_blkno;u16 bit; - ret =ocfs2_read_block (inode, block, &blk_bh); + ret =ocfs2_read_xattr_block (inode, block, &blk_bh);if (ret <0 ) {mlog_errno (ret); goto out; } - xb = (struct ocfs2_xattr_block *)blk_bh->b_data; -if (!OCFS2_IS_VALID_XATTR_BLOCK (xb)) { - ret = -EIO ; - goto out; - } - ret =ocfs2_xattr_block_remove (inode, blk_bh);if (ret <0 ) {mlog_errno (ret); goto out; } + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; blk =le64_to_cpu (xb->xb_blkno); bit =le16_to_cpu (xb->xb_suballoc_bit); bg_blkno =ocfs2_which_suballoc_group (blk, bit);
@@ -1793,19 +1843,15 @@ static int ocfs2_xattr_block_find(struct inode *inode,if (!di->i_xattr_loc)return ret; - ret =ocfs2_read_block (inode,le64_to_cpu (di->i_xattr_loc), &blk_bh); + ret =ocfs2_read_xattr_block (inode,le64_to_cpu (di->i_xattr_loc), + &blk_bh);if (ret <0 ) {mlog_errno (ret);return ret; } - xb = (struct ocfs2_xattr_block *)blk_bh->b_data; -if (!OCFS2_IS_VALID_XATTR_BLOCK (xb)) { - ret = -EIO ; - goto cleanup; - } - xs->xattr_bh = blk_bh; + xb = (struct ocfs2_xattr_block *)blk_bh->b_data;if (!(le16_to_cpu (xb->xb_flags) &OCFS2_XATTR_INDEXED )) { xs->header = &xb->xb_attrs.xb_header;
@@ -2102,9 +2148,9 @@ meta_guess:/* calculate metadata allocation. */ if (di->i_xattr_loc) {if (!xbs->xattr_bh) { - ret =ocfs2_read_block (inode, -le64_to_cpu (di->i_xattr_loc), - &bh); + ret =ocfs2_read_xattr_block (inode, +le64_to_cpu (di->i_xattr_loc), + &bh);if (ret) {mlog_errno (ret); goto out;
Signature · 1 line
1.5.6.5
PATCH 9/11 ocfs2: Validate metadata only when it's read from disk.
In reply to Joel BeckerAdd an optional validation hook to ocfs2_read_blocks(). Now the validation function is only called when a block was actually read off of disk. It is not called when the buffer was in cache.
We add a buffer state bit BH_NeedsValidate to flag these buffers. It must always be one higher than the last JBD2 buffer state bit.
The dinode, dirblock, extent_block, and xattr_block validators are lifted to this scheme directly. The group_descriptor validator needs to be split into two pieces. The first part only needs the gd buffer and is passed to ocfs2_read_block(). The second part requires the dinode as well, and is called every time. It's only 3 compares, so it's tiny. This also allows us to clean up the non-fatal gd check used by resize.c. It now has no magic argument.
--- fs/ocfs2/alloc.c | 17 +++----- fs/ocfs2/buffer_head_io.c | 33 ++++++++++++++++- fs/ocfs2/buffer_head_io.h | 27 ++++++++----- fs/ocfs2/dir.c | 13 ++---- fs/ocfs2/inode.c | 18 ++------ fs/ocfs2/resize.c | 2 +- fs/ocfs2/slot_map.c | 4 +- fs/ocfs2/suballoc.c | 91 +++++++++++++++++++++++++++++++------------- fs/ocfs2/suballoc.h | 15 +++---- fs/ocfs2/xattr.c | 26 ++++++------- 10 files changed, 149 insertions(+), 97 deletions(-)
fs/ocfs2/alloc.c +6 -11
@@ -684,6 +684,9 @@ static int ocfs2_validate_extent_block(struct super_block *sb,struct ocfs2_extent_block *eb = (struct ocfs2_extent_block *)bh->b_data; +mlog (0 ,"Validating extent block %llu\n" , + (unsigned long long )bh->b_blocknr); +if (!OCFS2_IS_VALID_EXTENT_BLOCK (eb)) { ocfs2_error(sb, "Extent block #%llu has bad signature %.*s",
@@ -719,21 +722,13 @@ int ocfs2_read_extent_block(struct inode *inode, u64 eb_blkno,int rc;struct buffer_head *tmp = *bh; - rc =ocfs2_read_block (inode, eb_blkno, &tmp); -if (rc) - goto out; - - rc =ocfs2_validate_extent_block (inode->i_sb, tmp); -if (rc) { -brelse (tmp); - goto out; - } + rc =ocfs2_read_block (inode, eb_blkno, &tmp, + ocfs2_validate_extent_block);/* If ocfs2_read_block() got us a new bh, pass it up. */ -if (!*bh) +if (!rc && !*bh) *bh = tmp; -out:return rc; }
fs/ocfs2/buffer_head_io.c +32 -1
@@ -39,6 +39,19 @@#include "buffer_head_io.h" +/* +* Bits on bh->b_state used by ocfs2. +* +* These MUST be after the JBD2 bits. Currently BH_Unshadow is the last +* JBD2 bit. +*/ +enum ocfs2_state_bits { + BH_NeedsValidate = BH_Unshadow +1 , +}; + +/* Expand the magic b_state functions */ +BUFFER_FNS (NeedsValidate, needs_validate); +int ocfs2_write_block (struct ocfs2_super *osb,struct buffer_head *bh,struct inode *inode) {
@@ -171,7 +184,9 @@ bail: }int ocfs2_read_blocks (struct inode *inode,u64 block,int nr, -struct buffer_head *bhs[],int flags) +struct buffer_head *bhs[],int flags, +int (*validate)(struct super_block *sb, +struct buffer_head *bh)) {int status =0 ;int i, ignore_cache =0 ;
@@ -305,6 +320,8 @@ int ocfs2_read_blocks(struct inode *inode, u64 block, int nr,clear_buffer_uptodate (bh);get_bh (bh);/* for end_buffer_read_sync() */ +if (validate) +set_buffer_needs_validate (bh); bh->b_end_io = end_buffer_read_sync;submit_bh (READ , bh);continue ;
@@ -335,6 +352,20 @@ int ocfs2_read_blocks(struct inode *inode, u64 block, int nr, bhs[i] =NULL ;continue ; } + +if (buffer_needs_validate (bh)) { +/* We never set NeedsValidate if the +* buffer was held by the journal, so +* that better not have changed */ +BUG_ON (buffer_jbd (bh)); +clear_buffer_needs_validate (bh); + status =validate (inode->i_sb, bh); +if (status) { +put_bh (bh); + bhs[i] =NULL ; +continue ; + } + } }/* Always set the buffer in the cache, even if it was
fs/ocfs2/buffer_head_io.h +16 -11
@@ -31,21 +31,24 @@void ocfs2_end_buffer_io_sync (struct buffer_head *bh,int uptodate); -static inline int ocfs2_read_block (struct inode *inode, -u64 off, -struct buffer_head **bh); -int ocfs2_write_block (struct ocfs2_super *osb,struct buffer_head *bh,struct inode *inode); -int ocfs2_read_blocks (struct inode *inode, -u64 block, -int nr, -struct buffer_head *bhs[], -int flags);int ocfs2_read_blocks_sync (struct ocfs2_super *osb,u64 block,unsigned int nr,struct buffer_head *bhs[]); +/* +* If not NULL, validate() will be called on a buffer that is freshly +* read from disk. It will not be called if the buffer was in cache. +* Note that if validate() is being used for this buffer, it needs to +* be set even for a READAHEAD call, as it marks the buffer for later +* validation. +*/ +int ocfs2_read_blocks (struct inode *inode,u64 block,int nr, +struct buffer_head *bhs[],int flags, +int (*validate)(struct super_block *sb, +struct buffer_head *bh)); +int ocfs2_write_super_or_backup (struct ocfs2_super *osb,struct buffer_head *bh);
@@ -53,7 +56,9 @@ int ocfs2_write_super_or_backup(struct ocfs2_super *osb,#define OCFS2_BH_READAHEAD 8static inline int ocfs2_read_block (struct inode *inode,u64 off, -struct buffer_head **bh) +struct buffer_head **bh, +int (*validate)(struct super_block *sb, +struct buffer_head *bh)) {int status =0 ;
@@ -63,7 +68,7 @@ static inline int ocfs2_read_block(struct inode *inode, u64 off, goto bail; } - status =ocfs2_read_blocks (inode, off,1 , bh,0 ); + status =ocfs2_read_blocks (inode, off,1 , bh,0 , validate); bail:return status;
fs/ocfs2/dir.c +4 -9
@@ -214,6 +214,8 @@ static int ocfs2_validate_dir_block(struct super_block *sb,* Nothing yet. We don't validate dirents here, that's handled * in-place when the code walks them. */ +mlog (0 ,"Validating dirblock %llu\n" , + (unsigned long long )bh->b_blocknr);return 0 ; }
@@ -255,20 +257,13 @@ static int ocfs2_read_dir_block(struct inode *inode, u64 v_block, goto out; } - rc =ocfs2_read_blocks (inode, p_blkno,1 , &tmp, flags); + rc =ocfs2_read_blocks (inode, p_blkno,1 , &tmp, flags, + ocfs2_validate_dir_block);if (rc) {mlog_errno (rc); goto out; } -if (!(flags &OCFS2_BH_READAHEAD )) { - rc =ocfs2_validate_dir_block (inode->i_sb, tmp); -if (rc) { -brelse (tmp); - goto out; - } - } -/* If ocfs2_read_blocks() got us a new bh, pass it up. */ if (!*bh) *bh = tmp;
fs/ocfs2/inode.c +5 -13
@@ -1255,6 +1255,9 @@ int ocfs2_validate_inode_block(struct super_block *sb,int rc = -EINVAL ;struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; +mlog (0 ,"Validating dinode %llu\n" , + (unsigned long long )bh->b_blocknr); +BUG_ON (!buffer_uptodate (bh));if (!OCFS2_IS_VALID_DINODE (di)) {
@@ -1300,23 +1303,12 @@ int ocfs2_read_inode_block_full(struct inode *inode, struct buffer_head **bh,struct buffer_head *tmp = *bh; rc =ocfs2_read_blocks (inode,OCFS2_I (inode)->ip_blkno,1 , &tmp, - flags); -if (rc) - goto out; - -if (!(flags &OCFS2_BH_READAHEAD )) { - rc =ocfs2_validate_inode_block (inode->i_sb, tmp); -if (rc) { -brelse (tmp); - goto out; - } - } + flags, ocfs2_validate_inode_block);/* If ocfs2_read_blocks() got us a new bh, pass it up. */ -if (!*bh) +if (!rc && !*bh) *bh = tmp; -out:return rc; }
fs/ocfs2/resize.c +1 -1
@@ -394,7 +394,7 @@ static int ocfs2_check_new_group(struct inode *inode, (struct ocfs2_group_desc *)group_bh->b_data;u16 cl_bpc =le16_to_cpu (di->id2.i_chain.cl_bpc); - ret =ocfs2_validate_group_descriptor (inode->i_sb, di, group_bh,1 ); + ret =ocfs2_check_group_descriptor (inode->i_sb, di, group_bh);if (ret) goto out;
fs/ocfs2/slot_map.c +2 -2
@@ -151,7 +151,7 @@ int ocfs2_refresh_slot_info(struct ocfs2_super *osb)* this is not true, the read of -1 (UINT64_MAX) will fail. */ ret =ocfs2_read_blocks (si->si_inode,-1 , si->si_blocks, si->si_bh, -OCFS2_BH_IGNORE_CACHE ); +OCFS2_BH_IGNORE_CACHE ,NULL );if (ret ==0 ) {spin_lock (&osb->osb_lock);ocfs2_update_slot_info (si);
@@ -405,7 +405,7 @@ static int ocfs2_map_slot_buffers(struct ocfs2_super *osb, bh =NULL ;/* Acquire a fresh bh */ status =ocfs2_read_blocks (si->si_inode, blkno,1 , &bh, -OCFS2_BH_IGNORE_CACHE ); +OCFS2_BH_IGNORE_CACHE ,NULL );if (status <0 ) {mlog_errno (status); goto bail;
fs/ocfs2/suballoc.c +64 -27
127 lines not on this page · raw message
fs/ocfs2/suballoc.h +7 -8
@@ -165,16 +165,15 @@ void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac);u64 ocfs2_which_cluster_group (struct inode *inode,u32 cluster);/* -* By default, ocfs2_validate_group_descriptor() calls ocfs2_error() when it +* By default, ocfs2_read_group_descriptor() calls ocfs2_error() when it * finds a problem. A caller that wants to check a group descriptor -* without going readonly passes a nonzero clean_error. This is only -* resize, really. Everyone else should be using -* ocfs2_read_group_descriptor(). +* without going readonly should read the block with ocfs2_read_block[s]() +* and then checking it with this function. This is only resize, really. +* Everyone else should be using ocfs2_read_group_descriptor(). */ -int ocfs2_validate_group_descriptor (struct super_block *sb, -struct ocfs2_dinode *di, -struct buffer_head *bh, -int clean_error); +int ocfs2_check_group_descriptor (struct super_block *sb, +struct ocfs2_dinode *di, +struct buffer_head *bh);/* * Read a group descriptor block into *bh. If *bh is NULL, a bh will be * allocated. This is a cached read. The descriptor will be validated with
fs/ocfs2/xattr.c +12 -14
@@ -246,7 +246,8 @@ static int ocfs2_read_xattr_bucket(struct ocfs2_xattr_bucket *bucket,int rc; rc =ocfs2_read_blocks (bucket->bu_inode, xb_blkno, - bucket->bu_blocks, bucket->bu_bhs,0 ); + bucket->bu_blocks, bucket->bu_bhs,0 , +NULL );if (rc)ocfs2_xattr_bucket_relse (bucket);return rc;
@@ -339,12 +340,8 @@ static int ocfs2_read_xattr_block(struct inode *inode, u64 xb_blkno,int rc;struct buffer_head *tmp = *bh; - rc =ocfs2_read_block (inode, xb_blkno, &tmp); -if (!rc) { - rc =ocfs2_validate_xattr_block (inode->i_sb, tmp); -if (rc) -brelse (tmp); - } + rc =ocfs2_read_block (inode, xb_blkno, &tmp, + ocfs2_validate_xattr_block);/* If ocfs2_read_block() got us a new bh, pass it up. */ if (!rc && !*bh)
@@ -784,7 +781,7 @@ static int ocfs2_xattr_get_value_outside(struct inode *inode, blkno =ocfs2_clusters_to_blocks (inode->i_sb, p_cluster);/* Copy ocfs2_xattr_value */ for (i =0 ; i < num_clusters * bpc; i++, blkno++) { - ret =ocfs2_read_block (inode, blkno, &bh); + ret =ocfs2_read_block (inode, blkno, &bh,NULL );if (ret) {mlog_errno (ret); goto out;
@@ -1017,7 +1014,7 @@ static int __ocfs2_xattr_set_value_outside(struct inode *inode, blkno =ocfs2_clusters_to_blocks (inode->i_sb, p_cluster);for (i =0 ; i < num_clusters * bpc; i++, blkno++) { - ret =ocfs2_read_block (inode, blkno, &bh); + ret =ocfs2_read_block (inode, blkno, &bh,NULL );if (ret) {mlog_errno (ret); goto out;
@@ -2049,7 +2046,7 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode, base = xis->base; credits +=OCFS2_INODE_UPDATE_CREDITS ; }else { -int i, block_off; +int i, block_off =0 ; xb = (struct ocfs2_xattr_block *)xbs->xattr_bh->b_data; xe = xbs->here; name_offset =le16_to_cpu (xe->xe_name_offset);
@@ -2615,6 +2612,7 @@ static int ocfs2_find_xe_in_bucket(struct inode *inode,break ; } + xe_name =bucket_block (bucket, block_off) + new_offset;if (!memcmp (name, xe_name, name_len)) { *xe_index = i;
@@ -3379,7 +3377,7 @@ static int ocfs2_mv_xattr_bucket_cross_cluster(struct inode *inode, goto out; } - ret =ocfs2_read_block (inode, prev_blkno, &old_bh); + ret =ocfs2_read_block (inode, prev_blkno, &old_bh,NULL );if (ret <0 ) {mlog_errno (ret);brelse (new_bh);
@@ -3771,7 +3769,7 @@ static int ocfs2_cp_xattr_cluster(struct inode *inode,ocfs2_journal_dirty (handle, first_bh);/* update the new bucket header. */ - ret =ocfs2_read_block (inode, to_blk_start, &bh); + ret =ocfs2_read_block (inode, to_blk_start, &bh,NULL );if (ret <0 ) {mlog_errno (ret); goto out;
@@ -4118,7 +4116,7 @@ static int ocfs2_add_new_xattr_bucket(struct inode *inode, goto out; } - ret =ocfs2_read_block (inode, p_blkno, &first_bh); + ret =ocfs2_read_block (inode, p_blkno, &first_bh,NULL );if (ret) {mlog_errno (ret); goto out;
@@ -4416,7 +4414,7 @@ static int ocfs2_xattr_bucket_value_truncate(struct inode *inode,BUG_ON (value_blk != (offset +OCFS2_XATTR_ROOT_SIZE -1 ) / blocksize); value_blk += header_bh->b_blocknr; - ret =ocfs2_read_block (inode, value_blk, &value_bh); + ret =ocfs2_read_block (inode, value_blk, &value_bh,NULL );if (ret) {mlog_errno (ret); goto out;
Signature · 1 line
1.5.6.5
PATCH 10/11 ocfs2: Wrap virtual block reads in ocfs2_read_virt_blocks()
In reply to Joel BeckerThe ocfs2_read_dir_block() function really maps an inode's virtual blocks to physical ones before calling ocfs2_read_blocks(). Let's extract that to common code, because other places might want to do that.
Other than the block number being virtual, ocfs2_read_virt_blocks() takes the same arguments as ocfs2_read_blocks(). It converts those virtual block numbers to physical before calling ocfs2_read_blocks() directly. If the blocks asked for are discontiguous, this can mean multiple calls to ocfs2_read_blocks(), but this is mostly hidden from the caller.
Like ocfs2_read_blocks(), the caller can pass in an existing buffer_head. This is usually done to pick up some readahead I/O. ocfs2_read_virt_blocks() checks the buffer_head's block number against the extent map - it must match.
--- fs/ocfs2/extent_map.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/extent_map.h | 24 ++++++++++++++++ 2 files changed, 95 insertions(+), 0 deletions(-)
fs/ocfs2/extent_map.c +71 -0
@@ -806,3 +806,74 @@ out:return ret; } + +int ocfs2_read_virt_blocks (struct inode *inode,u64 v_block,int nr, +struct buffer_head *bhs[],int flags, +int (*validate)(struct super_block *sb, +struct buffer_head *bh)) +{ +int rc =0 ; +u64 p_block, p_count; +int i, count, done =0 ; + +mlog_entry ("(inode = %p, v_block = %llu, nr = %d, bhs = %p, " +"flags = %x, validate = %p)\n" , + inode, (unsigned long long )v_block, nr, bhs, flags, + validate); + +if (((v_block + nr -1 ) << inode->i_sb->s_blocksize_bits) >= +i_size_read (inode)) { +BUG_ON (!(flags &OCFS2_BH_READAHEAD )); + goto out; + } + +while (done < nr) { +down_read (&OCFS2_I (inode)->ip_alloc_sem); + rc =ocfs2_extent_map_get_blocks (inode, v_block + done, + &p_block, &p_count,NULL ); +up_read (&OCFS2_I (inode)->ip_alloc_sem); +if (rc) { +mlog_errno (rc); +break ; + } + +if (!p_block) { + rc = -EIO ; +mlog (ML_ERROR , +"Inode #%llu contains a hole at offset %llu\n" , + (unsigned long long )OCFS2_I (inode)->ip_blkno, + (unsigned long long )(v_block + done) << + inode->i_sb->s_blocksize_bits); +break ; + } + + count = nr - done; +if (p_count < count) + count = p_count; + +/* +* If the caller passed us bhs, they should have come +* from a previous readahead call to this function. Thus, +* they should have the right b_blocknr. +*/ +for (i =0 ; i < count; i++) { +if (!bhs[done + i]) +continue ; +BUG_ON (bhs[done + i]->b_blocknr != (p_block + i)); + } + + rc =ocfs2_read_blocks (inode, p_block, count, bhs + done, + flags, validate); +if (rc) { +mlog_errno (rc); +break ; + } + done += count; + } + +out: +mlog_exit (rc); +return rc; +} + +
fs/ocfs2/extent_map.h +24 -0
@@ -57,4 +57,28 @@ int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster, u32 *num_clusters, struct ocfs2_extent_list *el); +int ocfs2_read_virt_blocks (struct inode *inode,u64 v_block,int nr, +struct buffer_head *bhs[],int flags, +int (*validate)(struct super_block *sb, +struct buffer_head *bh)); +static inline int ocfs2_read_virt_block (struct inode *inode,u64 v_block, +struct buffer_head **bh, +int (*validate)(struct super_block *sb, +struct buffer_head *bh)) +{ +int status =0 ; + +if (bh ==NULL ) { +printk ("ocfs2: bh == NULL\n" ); + status = -EINVAL ; + goto bail; + } + + status =ocfs2_read_virt_blocks (inode, v_block,1 , bh,0 , validate); + +bail: +return status; +} + +#endif /* _EXTENT_MAP_H */
Signature · 1 line
1.5.6.5
PATCH 11/11 ocfs2: Convert ocfs2_read_dir_block() to ocfs2_read_virt_blocks()
In reply to Joel BeckerNow that we've centralized the ocfs2_read_virt_blocks() code, let's use it in ocfs2_read_dir_block().
--- fs/ocfs2/dir.c | 38 +++++--------------------------------- 1 files changed, 5 insertions(+), 33 deletions(-)
fs/ocfs2/dir.c +5 -33
@@ -231,44 +231,16 @@ static int ocfs2_read_dir_block(struct inode *inode, u64 v_block, {int rc =0 ;struct buffer_head *tmp = *bh; -u64 p_blkno; -if (((u64 )v_block << inode->i_sb->s_blocksize_bits) >= -i_size_read (inode)) { -BUG_ON (!(flags &OCFS2_BH_READAHEAD )); - goto out; - } - -down_read (&OCFS2_I (inode)->ip_alloc_sem); - rc =ocfs2_extent_map_get_blocks (inode, v_block, &p_blkno,NULL , -NULL ); -up_read (&OCFS2_I (inode)->ip_alloc_sem); -if (rc) { + rc =ocfs2_read_virt_blocks (inode, v_block,1 , &tmp, flags, + ocfs2_validate_dir_block); +if (rc)mlog_errno (rc); - goto out; - } -if (!p_blkno) { - rc = -EIO ; -mlog (ML_ERROR , -"Directory #%llu contains a hole at offset %llu\n" , - (unsigned long long )OCFS2_I (inode)->ip_blkno, - (unsigned long long )v_block << inode->i_sb->s_blocksize_bits); - goto out; - } - - rc =ocfs2_read_blocks (inode, p_blkno,1 , &tmp, flags, - ocfs2_validate_dir_block); -if (rc) { -mlog_errno (rc); - goto out; - } - -/* If ocfs2_read_blocks() got us a new bh, pass it up. */ -if (!*bh) +/* If ocfs2_read_virt_blocks() got us a new bh, pass it up. */ +if (!rc && !*bh) *bh = tmp; -out:return rc ? -EIO :0 ; }
Signature · 1 line
1.5.6.5
cover Dedicated metadata read functions
In reply to Joel BeckerQuoted 6 lines · Joel Becker
The following patch series builds on top of the
read_blockcleanups to provide dedicated functions to read the various types of ocfs2 metadata. Rather than calling ocfs2_read_block[s]() directly, functions wanting an inode now callocfs2_read_inode_block(). The same is true for extent blocks, group descriptors, and dirblocks. I will get to xattr blocks later.
Ack - xattr blocks are in this series now too. See:
Quoted 1 line
ocfs2: Wrap xattr block reads in a dedicated function
Joel
Signature · 9 lines
Life's Little Instruction Book #198 "Feed a stranger's expired parking meter." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127