mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00: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
@ -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 New Issue
Block a user