mirror of
https://github.com/torvalds/linux.git
synced 2026-05-22 06:01:53 +02:00
bcachefs: Fix dirent_casefold_mismatch repair
Instead of simply recreating a mis-casefolded dirent, use the str_hash repair code, which will rename it if necessary - the dirent might have been created again with the correct casefolding. Factor out out bch2_str_hash_repair key() from __bch2_str_hash_check_key() for the new path to use, and export bch2_dirent_create_key() as well. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
b938d3c970
commit
2bf380c005
|
|
@ -288,7 +288,7 @@ int bch2_dirent_init_name(struct bkey_i_dirent *dirent,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct bkey_i_dirent *dirent_create_key(struct btree_trans *trans,
|
||||
struct bkey_i_dirent *bch2_dirent_create_key(struct btree_trans *trans,
|
||||
const struct bch_hash_info *hash_info,
|
||||
subvol_inum dir,
|
||||
u8 type,
|
||||
|
|
@ -332,7 +332,7 @@ int bch2_dirent_create_snapshot(struct btree_trans *trans,
|
|||
struct bkey_i_dirent *dirent;
|
||||
int ret;
|
||||
|
||||
dirent = dirent_create_key(trans, hash_info, dir_inum, type, name, NULL, dst_inum);
|
||||
dirent = bch2_dirent_create_key(trans, hash_info, dir_inum, type, name, NULL, dst_inum);
|
||||
ret = PTR_ERR_OR_ZERO(dirent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -356,7 +356,7 @@ int bch2_dirent_create(struct btree_trans *trans, subvol_inum dir,
|
|||
struct bkey_i_dirent *dirent;
|
||||
int ret;
|
||||
|
||||
dirent = dirent_create_key(trans, hash_info, dir, type, name, NULL, dst_inum);
|
||||
dirent = bch2_dirent_create_key(trans, hash_info, dir, type, name, NULL, dst_inum);
|
||||
ret = PTR_ERR_OR_ZERO(dirent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -461,8 +461,8 @@ int bch2_dirent_rename(struct btree_trans *trans,
|
|||
*src_offset = dst_iter.pos.offset;
|
||||
|
||||
/* Create new dst key: */
|
||||
new_dst = dirent_create_key(trans, dst_hash, dst_dir, 0, dst_name,
|
||||
dst_hash->cf_encoding ? &dst_name_lookup : NULL, 0);
|
||||
new_dst = bch2_dirent_create_key(trans, dst_hash, dst_dir, 0, dst_name,
|
||||
dst_hash->cf_encoding ? &dst_name_lookup : NULL, 0);
|
||||
ret = PTR_ERR_OR_ZERO(new_dst);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
@ -472,8 +472,8 @@ int bch2_dirent_rename(struct btree_trans *trans,
|
|||
|
||||
/* Create new src key: */
|
||||
if (mode == BCH_RENAME_EXCHANGE) {
|
||||
new_src = dirent_create_key(trans, src_hash, src_dir, 0, src_name,
|
||||
src_hash->cf_encoding ? &src_name_lookup : NULL, 0);
|
||||
new_src = bch2_dirent_create_key(trans, src_hash, src_dir, 0, src_name,
|
||||
src_hash->cf_encoding ? &src_name_lookup : NULL, 0);
|
||||
ret = PTR_ERR_OR_ZERO(new_src);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ int bch2_dirent_init_name(struct bkey_i_dirent *,
|
|||
const struct bch_hash_info *,
|
||||
const struct qstr *,
|
||||
const struct qstr *);
|
||||
struct bkey_i_dirent *bch2_dirent_create_key(struct btree_trans *,
|
||||
const struct bch_hash_info *, subvol_inum, u8,
|
||||
const struct qstr *, const struct qstr *, u64);
|
||||
|
||||
int bch2_dirent_create_snapshot(struct btree_trans *, u32, u64, u32,
|
||||
const struct bch_hash_info *, u8,
|
||||
|
|
|
|||
|
|
@ -2210,32 +2210,34 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
|||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k),
|
||||
buf.buf))) {
|
||||
struct qstr name = bch2_dirent_get_name(d);
|
||||
u32 subvol = d.v->d_type == DT_SUBVOL
|
||||
? le32_to_cpu(d.v->d_parent_subvol)
|
||||
: 0;
|
||||
subvol_inum dir_inum = { .subvol = d.v->d_type == DT_SUBVOL
|
||||
? le32_to_cpu(d.v->d_parent_subvol)
|
||||
: 0,
|
||||
};
|
||||
u64 target = d.v->d_type == DT_SUBVOL
|
||||
? le32_to_cpu(d.v->d_child_subvol)
|
||||
: le64_to_cpu(d.v->d_inum);
|
||||
u64 dir_offset;
|
||||
struct qstr name = bch2_dirent_get_name(d);
|
||||
|
||||
ret = bch2_hash_delete_at(trans,
|
||||
struct bkey_i_dirent *new_d =
|
||||
bch2_dirent_create_key(trans, hash_info, dir_inum,
|
||||
d.v->d_type, &name, NULL, target);
|
||||
ret = PTR_ERR_OR_ZERO(new_d);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
new_d->k.p.inode = d.k->p.inode;
|
||||
new_d->k.p.snapshot = d.k->p.snapshot;
|
||||
|
||||
struct btree_iter dup_iter = {};
|
||||
ret = bch2_hash_delete_at(trans,
|
||||
bch2_dirent_hash_desc, hash_info, iter,
|
||||
BTREE_UPDATE_internal_snapshot_node) ?:
|
||||
bch2_dirent_create_snapshot(trans, subvol,
|
||||
d.k->p.inode, d.k->p.snapshot,
|
||||
hash_info,
|
||||
d.v->d_type,
|
||||
&name,
|
||||
target,
|
||||
&dir_offset,
|
||||
BTREE_ITER_with_updates|
|
||||
BTREE_UPDATE_internal_snapshot_node|
|
||||
STR_HASH_must_create) ?:
|
||||
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
|
||||
|
||||
if (dir_offset < k.k->p.offset)
|
||||
*need_second_pass = true;
|
||||
bch2_str_hash_repair_key(trans, s,
|
||||
&bch2_dirent_hash_desc, hash_info,
|
||||
iter, bkey_i_to_s_c(&new_d->k_i),
|
||||
&dup_iter, bkey_s_c_null,
|
||||
need_second_pass);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ static int bch2_dirent_has_target(struct btree_trans *trans, struct bkey_s_c_dir
|
|||
}
|
||||
}
|
||||
|
||||
static noinline int fsck_rename_dirent(struct btree_trans *trans,
|
||||
struct snapshots_seen *s,
|
||||
const struct bch_hash_desc desc,
|
||||
struct bch_hash_info *hash_info,
|
||||
struct bkey_s_c_dirent old,
|
||||
bool *updated_before_k_pos)
|
||||
static int bch2_fsck_rename_dirent(struct btree_trans *trans,
|
||||
struct snapshots_seen *s,
|
||||
const struct bch_hash_desc desc,
|
||||
struct bch_hash_info *hash_info,
|
||||
struct bkey_s_c_dirent old,
|
||||
bool *updated_before_k_pos)
|
||||
{
|
||||
struct qstr old_name = bch2_dirent_get_name(old);
|
||||
struct bkey_i_dirent *new = bch2_trans_kmalloc(trans, BKEY_U64s_MAX * sizeof(u64));
|
||||
|
|
@ -233,54 +233,20 @@ static noinline int check_inode_hash_info_matches_root(struct btree_trans *trans
|
|||
return ret;
|
||||
}
|
||||
|
||||
int __bch2_str_hash_check_key(struct btree_trans *trans,
|
||||
struct snapshots_seen *s,
|
||||
const struct bch_hash_desc *desc,
|
||||
struct bch_hash_info *hash_info,
|
||||
struct btree_iter *k_iter, struct bkey_s_c hash_k,
|
||||
bool *updated_before_k_pos)
|
||||
/* Put a str_hash key in its proper location, checking for duplicates */
|
||||
int bch2_str_hash_repair_key(struct btree_trans *trans,
|
||||
struct snapshots_seen *s,
|
||||
const struct bch_hash_desc *desc,
|
||||
struct bch_hash_info *hash_info,
|
||||
struct btree_iter *k_iter, struct bkey_s_c k,
|
||||
struct btree_iter *dup_iter, struct bkey_s_c dup_k,
|
||||
bool *updated_before_k_pos)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_iter iter = {};
|
||||
struct printbuf buf = PRINTBUF;
|
||||
struct bkey_s_c k;
|
||||
bool free_snapshots_seen = false;
|
||||
int ret = 0;
|
||||
|
||||
u64 hash = desc->hash_bkey(hash_info, hash_k);
|
||||
if (hash_k.k->p.offset < hash)
|
||||
goto bad_hash;
|
||||
|
||||
for_each_btree_key_norestart(trans, iter, desc->btree_id,
|
||||
SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
|
||||
BTREE_ITER_slots|
|
||||
BTREE_ITER_with_updates, k, ret) {
|
||||
if (bkey_eq(k.k->p, hash_k.k->p))
|
||||
break;
|
||||
|
||||
if (k.k->type == desc->key_type &&
|
||||
!desc->cmp_bkey(k, hash_k))
|
||||
goto duplicate_entries;
|
||||
|
||||
if (bkey_deleted(k.k)) {
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
goto bad_hash;
|
||||
}
|
||||
}
|
||||
out:
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
printbuf_exit(&buf);
|
||||
if (free_snapshots_seen)
|
||||
darray_exit(&s->ids);
|
||||
return ret;
|
||||
bad_hash:
|
||||
/*
|
||||
* Before doing any repair, check hash_info itself:
|
||||
*/
|
||||
ret = check_inode_hash_info_matches_root(trans, hash_k.k->p.inode, hash_info);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!s) {
|
||||
s = bch2_trans_kmalloc(trans, sizeof(*s));
|
||||
ret = PTR_ERR_OR_ZERO(s);
|
||||
|
|
@ -297,25 +263,22 @@ int __bch2_str_hash_check_key(struct btree_trans *trans,
|
|||
free_snapshots_seen = true;
|
||||
}
|
||||
|
||||
if (fsck_err(trans, hash_table_key_wrong_offset,
|
||||
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s",
|
||||
bch2_btree_id_str(desc->btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
|
||||
struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, hash_k);
|
||||
if (IS_ERR(new))
|
||||
return PTR_ERR(new);
|
||||
if (!dup_k.k) {
|
||||
struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, k);
|
||||
ret = PTR_ERR_OR_ZERO(new);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
k = bch2_hash_set_or_get_in_snapshot(trans, &iter, *desc, hash_info,
|
||||
(subvol_inum) { 0, hash_k.k->p.inode },
|
||||
hash_k.k->p.snapshot, new,
|
||||
dup_k = bch2_hash_set_or_get_in_snapshot(trans, dup_iter, *desc, hash_info,
|
||||
(subvol_inum) { 0, new->k.p.inode },
|
||||
new->k.p.snapshot, new,
|
||||
STR_HASH_must_create|
|
||||
BTREE_ITER_with_updates|
|
||||
BTREE_UPDATE_internal_snapshot_node);
|
||||
ret = bkey_err(k);
|
||||
ret = bkey_err(dup_k);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (k.k)
|
||||
if (dup_k.k)
|
||||
goto duplicate_entries;
|
||||
|
||||
if (bpos_lt(new->k.p, k.k->p))
|
||||
|
|
@ -329,40 +292,108 @@ int __bch2_str_hash_check_key(struct btree_trans *trans,
|
|||
bch2_fsck_update_backpointers(trans, s, *desc, hash_info, new) ?:
|
||||
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
|
||||
-BCH_ERR_transaction_restart_commit;
|
||||
goto out;
|
||||
}
|
||||
fsck_err:
|
||||
goto out;
|
||||
} else {
|
||||
duplicate_entries:
|
||||
ret = hash_pick_winner(trans, *desc, hash_info, hash_k, k);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = hash_pick_winner(trans, *desc, hash_info, k, dup_k);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (!fsck_err(trans, hash_table_key_duplicate,
|
||||
"duplicate hash table keys%s:\n%s",
|
||||
ret != 2 ? "" : ", both point to valid inodes",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, hash_k),
|
||||
prt_newline(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k),
|
||||
buf.buf)))
|
||||
goto out;
|
||||
if (!fsck_err(trans, hash_table_key_duplicate,
|
||||
"duplicate hash table keys%s:\n%s",
|
||||
ret != 2 ? "" : ", both point to valid inodes",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, k),
|
||||
prt_newline(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, dup_k),
|
||||
buf.buf)))
|
||||
goto out;
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
ret = bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0);
|
||||
break;
|
||||
case 1:
|
||||
ret = bch2_hash_delete_at(trans, *desc, hash_info, &iter, 0);
|
||||
break;
|
||||
case 2:
|
||||
ret = fsck_rename_dirent(trans, s, *desc, hash_info, bkey_s_c_to_dirent(hash_k),
|
||||
updated_before_k_pos) ?:
|
||||
bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0);
|
||||
goto out;
|
||||
switch (ret) {
|
||||
case 0:
|
||||
ret = bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0);
|
||||
break;
|
||||
case 1:
|
||||
ret = bch2_hash_delete_at(trans, *desc, hash_info, dup_iter, 0);
|
||||
break;
|
||||
case 2:
|
||||
ret = bch2_fsck_rename_dirent(trans, s, *desc, hash_info,
|
||||
bkey_s_c_to_dirent(k),
|
||||
updated_before_k_pos) ?:
|
||||
bch2_hash_delete_at(trans, *desc, hash_info, k_iter,
|
||||
BTREE_ITER_with_updates);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
|
||||
-BCH_ERR_transaction_restart_commit;
|
||||
}
|
||||
out:
|
||||
fsck_err:
|
||||
bch2_trans_iter_exit(trans, dup_iter);
|
||||
printbuf_exit(&buf);
|
||||
if (free_snapshots_seen)
|
||||
darray_exit(&s->ids);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
|
||||
-BCH_ERR_transaction_restart_commit;
|
||||
int __bch2_str_hash_check_key(struct btree_trans *trans,
|
||||
struct snapshots_seen *s,
|
||||
const struct bch_hash_desc *desc,
|
||||
struct bch_hash_info *hash_info,
|
||||
struct btree_iter *k_iter, struct bkey_s_c hash_k,
|
||||
bool *updated_before_k_pos)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_iter iter = {};
|
||||
struct printbuf buf = PRINTBUF;
|
||||
struct bkey_s_c k;
|
||||
int ret = 0;
|
||||
|
||||
u64 hash = desc->hash_bkey(hash_info, hash_k);
|
||||
if (hash_k.k->p.offset < hash)
|
||||
goto bad_hash;
|
||||
|
||||
for_each_btree_key_norestart(trans, iter, desc->btree_id,
|
||||
SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
|
||||
BTREE_ITER_slots|
|
||||
BTREE_ITER_with_updates, k, ret) {
|
||||
if (bkey_eq(k.k->p, hash_k.k->p))
|
||||
break;
|
||||
|
||||
if (k.k->type == desc->key_type &&
|
||||
!desc->cmp_bkey(k, hash_k)) {
|
||||
ret = check_inode_hash_info_matches_root(trans, hash_k.k->p.inode,
|
||||
hash_info) ?:
|
||||
bch2_str_hash_repair_key(trans, s, desc, hash_info,
|
||||
k_iter, hash_k,
|
||||
&iter, k, updated_before_k_pos);
|
||||
break;
|
||||
}
|
||||
|
||||
if (bkey_deleted(k.k))
|
||||
goto bad_hash;
|
||||
}
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
out:
|
||||
fsck_err:
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
bad_hash:
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
/*
|
||||
* Before doing any repair, check hash_info itself:
|
||||
*/
|
||||
ret = check_inode_hash_info_matches_root(trans, hash_k.k->p.inode, hash_info);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (fsck_err(trans, hash_table_key_wrong_offset,
|
||||
"hash table key at wrong offset: should be at %llu\n%s",
|
||||
hash,
|
||||
(bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf)))
|
||||
ret = bch2_str_hash_repair_key(trans, s, desc, hash_info,
|
||||
k_iter, hash_k,
|
||||
&iter, bkey_s_c_null,
|
||||
updated_before_k_pos);
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -398,6 +398,14 @@ int bch2_hash_delete(struct btree_trans *trans,
|
|||
int bch2_repair_inode_hash_info(struct btree_trans *, struct bch_inode_unpacked *);
|
||||
|
||||
struct snapshots_seen;
|
||||
int bch2_str_hash_repair_key(struct btree_trans *,
|
||||
struct snapshots_seen *,
|
||||
const struct bch_hash_desc *,
|
||||
struct bch_hash_info *,
|
||||
struct btree_iter *, struct bkey_s_c,
|
||||
struct btree_iter *, struct bkey_s_c,
|
||||
bool *);
|
||||
|
||||
int __bch2_str_hash_check_key(struct btree_trans *,
|
||||
struct snapshots_seen *,
|
||||
const struct bch_hash_desc *,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user