mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	bcachefs: Add checks for invalid snapshot IDs
Previously, we assumed that keys were consistent with the snapshots btree - but that's not correct as fsck may not have been run or may not be complete. This adds checks and error handling when using the in-memory snapshots table (that mirrors the snapshots btree). Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
		
							parent
							
								
									63332394c7
								
							
						
					
					
						commit
						ec9cc18fc2
					
				
					 5 changed files with 40 additions and 39 deletions
				
			
		| 
						 | 
				
			
			@ -318,7 +318,7 @@ static inline void btree_insert_entry_checks(struct btree_trans *trans,
 | 
			
		|||
		!(i->flags & BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) &&
 | 
			
		||||
		test_bit(JOURNAL_REPLAY_DONE, &trans->c->journal.flags) &&
 | 
			
		||||
		i->k->k.p.snapshot &&
 | 
			
		||||
		bch2_snapshot_is_internal_node(trans->c, i->k->k.p.snapshot));
 | 
			
		||||
		bch2_snapshot_is_internal_node(trans->c, i->k->k.p.snapshot) > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __always_inline int bch2_trans_journal_res_get(struct btree_trans *trans,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@
 | 
			
		|||
#include "move.h"
 | 
			
		||||
#include "nocow_locking.h"
 | 
			
		||||
#include "rebalance.h"
 | 
			
		||||
#include "snapshot.h"
 | 
			
		||||
#include "subvolume.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -509,6 +510,14 @@ int bch2_data_update_init(struct btree_trans *trans,
 | 
			
		|||
	unsigned ptrs_locked = 0;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * fs is corrupt  we have a key for a snapshot node that doesn't exist,
 | 
			
		||||
	 * and we have to check for this because we go rw before repairing the
 | 
			
		||||
	 * snapshots table - just skip it, we can move it later.
 | 
			
		||||
	 */
 | 
			
		||||
	if (unlikely(k.k->p.snapshot && !bch2_snapshot_equiv(c, k.k->p.snapshot)))
 | 
			
		||||
		return -BCH_ERR_data_update_done;
 | 
			
		||||
 | 
			
		||||
	bch2_bkey_buf_init(&m->k);
 | 
			
		||||
	bch2_bkey_buf_reassemble(&m->k, c, k);
 | 
			
		||||
	m->btree_id	= btree_id;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -252,7 +252,8 @@
 | 
			
		|||
	x(BCH_ERR_nopromote,		nopromote_in_flight)			\
 | 
			
		||||
	x(BCH_ERR_nopromote,		nopromote_no_writes)			\
 | 
			
		||||
	x(BCH_ERR_nopromote,		nopromote_enomem)			\
 | 
			
		||||
	x(0,				need_inode_lock)
 | 
			
		||||
	x(0,				need_inode_lock)			\
 | 
			
		||||
	x(0,				invalid_snapshot_node)
 | 
			
		||||
 | 
			
		||||
enum bch_errcode {
 | 
			
		||||
	BCH_ERR_START		= 2048,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,8 +93,10 @@ static int bch2_snapshot_tree_create(struct btree_trans *trans,
 | 
			
		|||
 | 
			
		||||
static bool __bch2_snapshot_is_ancestor_early(struct snapshot_table *t, u32 id, u32 ancestor)
 | 
			
		||||
{
 | 
			
		||||
	while (id && id < ancestor)
 | 
			
		||||
		id = __snapshot_t(t, id)->parent;
 | 
			
		||||
	while (id && id < ancestor) {
 | 
			
		||||
		const struct snapshot_t *s = __snapshot_t(t, id);
 | 
			
		||||
		id = s ? s->parent : 0;
 | 
			
		||||
	}
 | 
			
		||||
	return id == ancestor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +112,8 @@ static bool bch2_snapshot_is_ancestor_early(struct bch_fs *c, u32 id, u32 ancest
 | 
			
		|||
static inline u32 get_ancestor_below(struct snapshot_table *t, u32 id, u32 ancestor)
 | 
			
		||||
{
 | 
			
		||||
	const struct snapshot_t *s = __snapshot_t(t, id);
 | 
			
		||||
	if (!s)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (s->skip[2] <= ancestor)
 | 
			
		||||
		return s->skip[2];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,8 @@ static inline const struct snapshot_t *snapshot_t(struct bch_fs *c, u32 id)
 | 
			
		|||
static inline u32 bch2_snapshot_tree(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
	rcu_read_lock();
 | 
			
		||||
	id = snapshot_t(c, id)->tree;
 | 
			
		||||
	const struct snapshot_t *s = snapshot_t(c, id);
 | 
			
		||||
	id = s ? s->tree : 0;
 | 
			
		||||
	rcu_read_unlock();
 | 
			
		||||
 | 
			
		||||
	return id;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +57,8 @@ static inline u32 bch2_snapshot_tree(struct bch_fs *c, u32 id)
 | 
			
		|||
 | 
			
		||||
static inline u32 __bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
	return snapshot_t(c, id)->parent;
 | 
			
		||||
	const struct snapshot_t *s = snapshot_t(c, id);
 | 
			
		||||
	return s ? s->parent : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
 | 
			
		||||
| 
						 | 
				
			
			@ -70,19 +72,19 @@ static inline u32 bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
 | 
			
		|||
 | 
			
		||||
static inline u32 __bch2_snapshot_parent(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_BCACHEFS_DEBUG
 | 
			
		||||
	u32 parent = snapshot_t(c, id)->parent;
 | 
			
		||||
	const struct snapshot_t *s = snapshot_t(c, id);
 | 
			
		||||
	if (!s)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (parent &&
 | 
			
		||||
	    snapshot_t(c, id)->depth != snapshot_t(c, parent)->depth + 1)
 | 
			
		||||
	u32 parent = s->parent;
 | 
			
		||||
	if (IS_ENABLED(CONFIG_BCACHEFS_DEBU) &&
 | 
			
		||||
	    parent &&
 | 
			
		||||
	    s->depth != snapshot_t(c, parent)->depth + 1)
 | 
			
		||||
		panic("id %u depth=%u parent %u depth=%u\n",
 | 
			
		||||
		      id, snapshot_t(c, id)->depth,
 | 
			
		||||
		      parent, snapshot_t(c, parent)->depth);
 | 
			
		||||
 | 
			
		||||
	return parent;
 | 
			
		||||
#else
 | 
			
		||||
	return snapshot_t(c, id)->parent;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 bch2_snapshot_parent(struct bch_fs *c, u32 id)
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +122,8 @@ static inline u32 bch2_snapshot_root(struct bch_fs *c, u32 id)
 | 
			
		|||
 | 
			
		||||
static inline u32 __bch2_snapshot_equiv(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
	return snapshot_t(c, id)->equiv;
 | 
			
		||||
	const struct snapshot_t *s = snapshot_t(c, id);
 | 
			
		||||
	return s ? s->equiv : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 bch2_snapshot_equiv(struct bch_fs *c, u32 id)
 | 
			
		||||
| 
						 | 
				
			
			@ -137,38 +140,22 @@ static inline bool bch2_snapshot_is_equiv(struct bch_fs *c, u32 id)
 | 
			
		|||
	return id == bch2_snapshot_equiv(c, id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool bch2_snapshot_is_internal_node(struct bch_fs *c, u32 id)
 | 
			
		||||
static inline int bch2_snapshot_is_internal_node(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
	const struct snapshot_t *s;
 | 
			
		||||
	bool ret;
 | 
			
		||||
 | 
			
		||||
	rcu_read_lock();
 | 
			
		||||
	s = snapshot_t(c, id);
 | 
			
		||||
	ret = s->children[0];
 | 
			
		||||
	const struct snapshot_t *s = snapshot_t(c, id);
 | 
			
		||||
	int ret = s ? s->children[0] : -BCH_ERR_invalid_snapshot_node;
 | 
			
		||||
	rcu_read_unlock();
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 bch2_snapshot_is_leaf(struct bch_fs *c, u32 id)
 | 
			
		||||
static inline int bch2_snapshot_is_leaf(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
	return !bch2_snapshot_is_internal_node(c, id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 bch2_snapshot_sibling(struct bch_fs *c, u32 id)
 | 
			
		||||
{
 | 
			
		||||
	const struct snapshot_t *s;
 | 
			
		||||
	u32 parent = __bch2_snapshot_parent(c, id);
 | 
			
		||||
 | 
			
		||||
	if (!parent)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	s = snapshot_t(c, __bch2_snapshot_parent(c, id));
 | 
			
		||||
	if (id == s->children[0])
 | 
			
		||||
		return s->children[1];
 | 
			
		||||
	if (id == s->children[1])
 | 
			
		||||
		return s->children[0];
 | 
			
		||||
	return 0;
 | 
			
		||||
	int ret = bch2_snapshot_is_internal_node(c, id);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
	return !ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 bch2_snapshot_depth(struct bch_fs *c, u32 parent)
 | 
			
		||||
| 
						 | 
				
			
			@ -253,7 +240,7 @@ static inline int bch2_key_has_snapshot_overwrites(struct btree_trans *trans,
 | 
			
		|||
					  struct bpos pos)
 | 
			
		||||
{
 | 
			
		||||
	if (!btree_type_has_snapshots(id) ||
 | 
			
		||||
	    bch2_snapshot_is_leaf(trans->c, pos.snapshot))
 | 
			
		||||
	    bch2_snapshot_is_leaf(trans->c, pos.snapshot) > 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return __bch2_key_has_snapshot_overwrites(trans, id, pos);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue