Skip to content

Commit

Permalink
nilfs2: verify btree node after reading
Browse files Browse the repository at this point in the history
This inserts sanity checks soon after read btree node from disk.  This
allows early detection of broken btree nodes, and helps to narrow down
problems due to file system corruption.

Signed-off-by: Ryusuke Konishi <[email protected]>
  • Loading branch information
konis committed Jul 23, 2010
1 parent cfa913a commit 1d5385b
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
50 changes: 47 additions & 3 deletions fs/nilfs2/btree.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,24 @@ static int nilfs_btree_get_block(const struct nilfs_btree *btree, __u64 ptr,
{
struct address_space *btnc =
&NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache;
struct buffer_head *bh;
int err;

err = nilfs_btnode_submit_block(btnc, ptr, 0, bhp);
if (err)
return err == -EEXIST ? 0 : err;

wait_on_buffer(*bhp);
if (!buffer_uptodate(*bhp)) {
brelse(*bhp);
bh = *bhp;
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
brelse(bh);
return -EIO;
}
if (nilfs_btree_broken_node_block(bh)) {
clear_buffer_uptodate(bh);
brelse(bh);
return -EINVAL;
}
return 0;
}

Expand Down Expand Up @@ -382,6 +389,43 @@ static int nilfs_btree_node_lookup(const struct nilfs_btree_node *node,
return s == 0;
}

/**
* nilfs_btree_node_broken - verify consistency of btree node
* @node: btree node block to be examined
* @size: node size (in bytes)
* @blocknr: block number
*
* Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned.
*/
static int nilfs_btree_node_broken(const struct nilfs_btree_node *node,
size_t size, sector_t blocknr)
{
int level, flags, nchildren;
int ret = 0;

level = nilfs_btree_node_get_level(node);
flags = nilfs_btree_node_get_flags(node);
nchildren = nilfs_btree_node_get_nchildren(node);

if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN ||
level >= NILFS_BTREE_LEVEL_MAX ||
(flags & NILFS_BTREE_NODE_ROOT) ||
nchildren < 0 ||
nchildren > NILFS_BTREE_NODE_NCHILDREN_MAX(size))) {
printk(KERN_CRIT "NILFS: bad btree node (blocknr=%llu): "
"level = %d, flags = 0x%x, nchildren = %d\n",
(unsigned long long)blocknr, level, flags, nchildren);
ret = 1;
}
return ret;
}

int nilfs_btree_broken_node_block(struct buffer_head *bh)
{
return nilfs_btree_node_broken((struct nilfs_btree_node *)bh->b_data,
bh->b_size, bh->b_blocknr);
}

static inline struct nilfs_btree_node *
nilfs_btree_get_root(const struct nilfs_btree *btree)
{
Expand Down
2 changes: 2 additions & 0 deletions fs/nilfs2/btree.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,6 @@ int nilfs_btree_convert_and_insert(struct nilfs_bmap *, __u64, __u64,
const __u64 *, const __u64 *, int);
void nilfs_btree_init_gc(struct nilfs_bmap *);

int nilfs_btree_broken_node_block(struct buffer_head *bh);

#endif /* _NILFS_BTREE_H */
9 changes: 7 additions & 2 deletions fs/nilfs2/gcinode.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,15 @@ int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *bh)
if (buffer_dirty(bh))
return -EEXIST;

if (buffer_nilfs_node(bh))
if (buffer_nilfs_node(bh)) {
if (nilfs_btree_broken_node_block(bh)) {
clear_buffer_uptodate(bh);
return -EIO;
}
nilfs_btnode_mark_dirty(bh);
else
} else {
nilfs_mdt_mark_buffer_dirty(bh);
}
return 0;
}

Expand Down

0 comments on commit 1d5385b

Please sign in to comment.