diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2021-10-19 12:27:47 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2022-04-17 15:44:09 -0400 |
commit | 9ab6cc3c718e1a58e80b503b1c5cc8aa97998634 (patch) | |
tree | fdbcdc6ea538ad1bca27f386cda8ecfc87c6c687 | |
parent | ea14a933c06d7ce002c6e5b93adae4235cfdd9b8 (diff) |
bcachefs: Improve reflink repair code
When a reflink pointer points to an indirect extent that doesn't exist,
we need to replace it with a KEY_TYPE_error key.
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/btree_gc.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/buckets.c | 51 |
2 files changed, 43 insertions, 10 deletions
diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c index 236ecbd82a63..8f6e73b1e260 100644 --- a/fs/bcachefs/btree_gc.c +++ b/fs/bcachefs/btree_gc.c @@ -738,7 +738,7 @@ static int bch2_gc_mark_key(struct bch_fs *c, enum btree_id btree_id, *max_stale = max(*max_stale, ptr_stale(ca, ptr)); } - bch2_mark_key(c, *k, flags); + ret = bch2_mark_key(c, *k, flags); fsck_err: err: if (ret) diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c index 5fd3aabb7669..d5ec4d727d0e 100644 --- a/fs/bcachefs/buckets.c +++ b/fs/bcachefs/buckets.c @@ -14,6 +14,7 @@ #include "ec.h" #include "error.h" #include "movinggc.h" +#include "recovery.h" #include "reflink.h" #include "replicas.h" #include "subvolume.h" @@ -1111,10 +1112,9 @@ static s64 __bch2_mark_reflink_p(struct bch_fs *c, struct bkey_s_c_reflink_p p, { struct reflink_gc *r; int add = !(flags & BTREE_TRIGGER_OVERWRITE) ? 1 : -1; + s64 ret = 0; - while (1) { - if (*r_idx >= c->reflink_gc_nr) - goto not_found; + while (*r_idx < c->reflink_gc_nr) { r = genradix_ptr(&c->reflink_gc_table, *r_idx); BUG_ON(!r); @@ -1123,16 +1123,49 @@ static s64 __bch2_mark_reflink_p(struct bch_fs *c, struct bkey_s_c_reflink_p p, (*r_idx)++; } + if (*r_idx >= c->reflink_gc_nr || + idx < r->offset - r->size) { + ret = p.k->size; + goto not_found; + } + BUG_ON((s64) r->refcount + add < 0); r->refcount += add; return r->offset - idx; not_found: - bch2_fs_inconsistent(c, - "%llu:%llu len %u points to nonexistent indirect extent %llu", - p.k->p.inode, p.k->p.offset, p.k->size, idx); - bch2_inconsistent_error(c); - return -EIO; + if ((flags & BTREE_TRIGGER_GC) && + (flags & BTREE_TRIGGER_NOATOMIC)) { + /* + * XXX: we're replacing the entire reflink pointer with an error + * key, we should just be replacing the part that was missing: + */ + if (fsck_err(c, "%llu:%llu len %u points to nonexistent indirect extent %llu", + p.k->p.inode, p.k->p.offset, p.k->size, idx)) { + struct bkey_i_error *new; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + bch_err(c, "%s: error allocating new key", __func__); + return -ENOMEM; + } + + bkey_init(&new->k); + new->k.type = KEY_TYPE_error; + new->k.p = p.k->p; + new->k.size = p.k->size; + ret = bch2_journal_key_insert(c, BTREE_ID_extents, 0, &new->k_i); + + } + } else { + bch2_fs_inconsistent(c, + "%llu:%llu len %u points to nonexistent indirect extent %llu", + p.k->p.inode, p.k->p.offset, p.k->size, idx); + bch2_inconsistent_error(c); + ret = -EIO; + } +fsck_err: + return ret; } static int bch2_mark_reflink_p(struct bch_fs *c, @@ -1164,7 +1197,7 @@ static int bch2_mark_reflink_p(struct bch_fs *c, while (sectors) { ret = __bch2_mark_reflink_p(c, p, idx, flags, &l); - if (ret < 0) + if (ret <= 0) return ret; ret = min_t(s64, ret, sectors); |