Skip to content

Commit 2bf380c

Browse files
author
Kent Overstreet
committed
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 <[email protected]>
1 parent b938d3c commit 2bf380c

File tree

5 files changed

+162
-118
lines changed

5 files changed

+162
-118
lines changed

fs/bcachefs/dirent.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ int bch2_dirent_init_name(struct bkey_i_dirent *dirent,
288288
return 0;
289289
}
290290

291-
static struct bkey_i_dirent *dirent_create_key(struct btree_trans *trans,
291+
struct bkey_i_dirent *bch2_dirent_create_key(struct btree_trans *trans,
292292
const struct bch_hash_info *hash_info,
293293
subvol_inum dir,
294294
u8 type,
@@ -332,7 +332,7 @@ int bch2_dirent_create_snapshot(struct btree_trans *trans,
332332
struct bkey_i_dirent *dirent;
333333
int ret;
334334

335-
dirent = dirent_create_key(trans, hash_info, dir_inum, type, name, NULL, dst_inum);
335+
dirent = bch2_dirent_create_key(trans, hash_info, dir_inum, type, name, NULL, dst_inum);
336336
ret = PTR_ERR_OR_ZERO(dirent);
337337
if (ret)
338338
return ret;
@@ -356,7 +356,7 @@ int bch2_dirent_create(struct btree_trans *trans, subvol_inum dir,
356356
struct bkey_i_dirent *dirent;
357357
int ret;
358358

359-
dirent = dirent_create_key(trans, hash_info, dir, type, name, NULL, dst_inum);
359+
dirent = bch2_dirent_create_key(trans, hash_info, dir, type, name, NULL, dst_inum);
360360
ret = PTR_ERR_OR_ZERO(dirent);
361361
if (ret)
362362
return ret;
@@ -461,8 +461,8 @@ int bch2_dirent_rename(struct btree_trans *trans,
461461
*src_offset = dst_iter.pos.offset;
462462

463463
/* Create new dst key: */
464-
new_dst = dirent_create_key(trans, dst_hash, dst_dir, 0, dst_name,
465-
dst_hash->cf_encoding ? &dst_name_lookup : NULL, 0);
464+
new_dst = bch2_dirent_create_key(trans, dst_hash, dst_dir, 0, dst_name,
465+
dst_hash->cf_encoding ? &dst_name_lookup : NULL, 0);
466466
ret = PTR_ERR_OR_ZERO(new_dst);
467467
if (ret)
468468
goto out;
@@ -472,8 +472,8 @@ int bch2_dirent_rename(struct btree_trans *trans,
472472

473473
/* Create new src key: */
474474
if (mode == BCH_RENAME_EXCHANGE) {
475-
new_src = dirent_create_key(trans, src_hash, src_dir, 0, src_name,
476-
src_hash->cf_encoding ? &src_name_lookup : NULL, 0);
475+
new_src = bch2_dirent_create_key(trans, src_hash, src_dir, 0, src_name,
476+
src_hash->cf_encoding ? &src_name_lookup : NULL, 0);
477477
ret = PTR_ERR_OR_ZERO(new_src);
478478
if (ret)
479479
goto out;

fs/bcachefs/dirent.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ int bch2_dirent_init_name(struct bkey_i_dirent *,
6363
const struct bch_hash_info *,
6464
const struct qstr *,
6565
const struct qstr *);
66+
struct bkey_i_dirent *bch2_dirent_create_key(struct btree_trans *,
67+
const struct bch_hash_info *, subvol_inum, u8,
68+
const struct qstr *, const struct qstr *, u64);
6669

6770
int bch2_dirent_create_snapshot(struct btree_trans *, u32, u64, u32,
6871
const struct bch_hash_info *, u8,

fs/bcachefs/fsck.c

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,32 +2210,34 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
22102210
(printbuf_reset(&buf),
22112211
bch2_bkey_val_to_text(&buf, c, k),
22122212
buf.buf))) {
2213-
struct qstr name = bch2_dirent_get_name(d);
2214-
u32 subvol = d.v->d_type == DT_SUBVOL
2215-
? le32_to_cpu(d.v->d_parent_subvol)
2216-
: 0;
2213+
subvol_inum dir_inum = { .subvol = d.v->d_type == DT_SUBVOL
2214+
? le32_to_cpu(d.v->d_parent_subvol)
2215+
: 0,
2216+
};
22172217
u64 target = d.v->d_type == DT_SUBVOL
22182218
? le32_to_cpu(d.v->d_child_subvol)
22192219
: le64_to_cpu(d.v->d_inum);
2220-
u64 dir_offset;
2220+
struct qstr name = bch2_dirent_get_name(d);
2221+
2222+
struct bkey_i_dirent *new_d =
2223+
bch2_dirent_create_key(trans, hash_info, dir_inum,
2224+
d.v->d_type, &name, NULL, target);
2225+
ret = PTR_ERR_OR_ZERO(new_d);
2226+
if (ret)
2227+
goto out;
2228+
2229+
new_d->k.p.inode = d.k->p.inode;
2230+
new_d->k.p.snapshot = d.k->p.snapshot;
22212231

2222-
ret = bch2_hash_delete_at(trans,
2232+
struct btree_iter dup_iter = {};
2233+
ret = bch2_hash_delete_at(trans,
22232234
bch2_dirent_hash_desc, hash_info, iter,
22242235
BTREE_UPDATE_internal_snapshot_node) ?:
2225-
bch2_dirent_create_snapshot(trans, subvol,
2226-
d.k->p.inode, d.k->p.snapshot,
2227-
hash_info,
2228-
d.v->d_type,
2229-
&name,
2230-
target,
2231-
&dir_offset,
2232-
BTREE_ITER_with_updates|
2233-
BTREE_UPDATE_internal_snapshot_node|
2234-
STR_HASH_must_create) ?:
2235-
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
2236-
2237-
if (dir_offset < k.k->p.offset)
2238-
*need_second_pass = true;
2236+
bch2_str_hash_repair_key(trans, s,
2237+
&bch2_dirent_hash_desc, hash_info,
2238+
iter, bkey_i_to_s_c(&new_d->k_i),
2239+
&dup_iter, bkey_s_c_null,
2240+
need_second_pass);
22392241
goto out;
22402242
}
22412243

fs/bcachefs/str_hash.c

Lines changed: 122 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ static int bch2_dirent_has_target(struct btree_trans *trans, struct bkey_s_c_dir
3131
}
3232
}
3333

34-
static noinline int fsck_rename_dirent(struct btree_trans *trans,
35-
struct snapshots_seen *s,
36-
const struct bch_hash_desc desc,
37-
struct bch_hash_info *hash_info,
38-
struct bkey_s_c_dirent old,
39-
bool *updated_before_k_pos)
34+
static int bch2_fsck_rename_dirent(struct btree_trans *trans,
35+
struct snapshots_seen *s,
36+
const struct bch_hash_desc desc,
37+
struct bch_hash_info *hash_info,
38+
struct bkey_s_c_dirent old,
39+
bool *updated_before_k_pos)
4040
{
4141
struct qstr old_name = bch2_dirent_get_name(old);
4242
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
233233
return ret;
234234
}
235235

236-
int __bch2_str_hash_check_key(struct btree_trans *trans,
237-
struct snapshots_seen *s,
238-
const struct bch_hash_desc *desc,
239-
struct bch_hash_info *hash_info,
240-
struct btree_iter *k_iter, struct bkey_s_c hash_k,
241-
bool *updated_before_k_pos)
236+
/* Put a str_hash key in its proper location, checking for duplicates */
237+
int bch2_str_hash_repair_key(struct btree_trans *trans,
238+
struct snapshots_seen *s,
239+
const struct bch_hash_desc *desc,
240+
struct bch_hash_info *hash_info,
241+
struct btree_iter *k_iter, struct bkey_s_c k,
242+
struct btree_iter *dup_iter, struct bkey_s_c dup_k,
243+
bool *updated_before_k_pos)
242244
{
243245
struct bch_fs *c = trans->c;
244-
struct btree_iter iter = {};
245246
struct printbuf buf = PRINTBUF;
246-
struct bkey_s_c k;
247247
bool free_snapshots_seen = false;
248248
int ret = 0;
249249

250-
u64 hash = desc->hash_bkey(hash_info, hash_k);
251-
if (hash_k.k->p.offset < hash)
252-
goto bad_hash;
253-
254-
for_each_btree_key_norestart(trans, iter, desc->btree_id,
255-
SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
256-
BTREE_ITER_slots|
257-
BTREE_ITER_with_updates, k, ret) {
258-
if (bkey_eq(k.k->p, hash_k.k->p))
259-
break;
260-
261-
if (k.k->type == desc->key_type &&
262-
!desc->cmp_bkey(k, hash_k))
263-
goto duplicate_entries;
264-
265-
if (bkey_deleted(k.k)) {
266-
bch2_trans_iter_exit(trans, &iter);
267-
goto bad_hash;
268-
}
269-
}
270-
out:
271-
bch2_trans_iter_exit(trans, &iter);
272-
printbuf_exit(&buf);
273-
if (free_snapshots_seen)
274-
darray_exit(&s->ids);
275-
return ret;
276-
bad_hash:
277-
/*
278-
* Before doing any repair, check hash_info itself:
279-
*/
280-
ret = check_inode_hash_info_matches_root(trans, hash_k.k->p.inode, hash_info);
281-
if (ret)
282-
goto out;
283-
284250
if (!s) {
285251
s = bch2_trans_kmalloc(trans, sizeof(*s));
286252
ret = PTR_ERR_OR_ZERO(s);
@@ -297,25 +263,22 @@ int __bch2_str_hash_check_key(struct btree_trans *trans,
297263
free_snapshots_seen = true;
298264
}
299265

300-
if (fsck_err(trans, hash_table_key_wrong_offset,
301-
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s",
302-
bch2_btree_id_str(desc->btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
303-
(printbuf_reset(&buf),
304-
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
305-
struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, hash_k);
306-
if (IS_ERR(new))
307-
return PTR_ERR(new);
308-
309-
k = bch2_hash_set_or_get_in_snapshot(trans, &iter, *desc, hash_info,
310-
(subvol_inum) { 0, hash_k.k->p.inode },
311-
hash_k.k->p.snapshot, new,
266+
if (!dup_k.k) {
267+
struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, k);
268+
ret = PTR_ERR_OR_ZERO(new);
269+
if (ret)
270+
goto out;
271+
272+
dup_k = bch2_hash_set_or_get_in_snapshot(trans, dup_iter, *desc, hash_info,
273+
(subvol_inum) { 0, new->k.p.inode },
274+
new->k.p.snapshot, new,
312275
STR_HASH_must_create|
313276
BTREE_ITER_with_updates|
314277
BTREE_UPDATE_internal_snapshot_node);
315-
ret = bkey_err(k);
278+
ret = bkey_err(dup_k);
316279
if (ret)
317280
goto out;
318-
if (k.k)
281+
if (dup_k.k)
319282
goto duplicate_entries;
320283

321284
if (bpos_lt(new->k.p, k.k->p))
@@ -329,40 +292,108 @@ int __bch2_str_hash_check_key(struct btree_trans *trans,
329292
bch2_fsck_update_backpointers(trans, s, *desc, hash_info, new) ?:
330293
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
331294
-BCH_ERR_transaction_restart_commit;
332-
goto out;
295+
} else {
296+
duplicate_entries:
297+
ret = hash_pick_winner(trans, *desc, hash_info, k, dup_k);
298+
if (ret < 0)
299+
goto out;
300+
301+
if (!fsck_err(trans, hash_table_key_duplicate,
302+
"duplicate hash table keys%s:\n%s",
303+
ret != 2 ? "" : ", both point to valid inodes",
304+
(printbuf_reset(&buf),
305+
bch2_bkey_val_to_text(&buf, c, k),
306+
prt_newline(&buf),
307+
bch2_bkey_val_to_text(&buf, c, dup_k),
308+
buf.buf)))
309+
goto out;
310+
311+
switch (ret) {
312+
case 0:
313+
ret = bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0);
314+
break;
315+
case 1:
316+
ret = bch2_hash_delete_at(trans, *desc, hash_info, dup_iter, 0);
317+
break;
318+
case 2:
319+
ret = bch2_fsck_rename_dirent(trans, s, *desc, hash_info,
320+
bkey_s_c_to_dirent(k),
321+
updated_before_k_pos) ?:
322+
bch2_hash_delete_at(trans, *desc, hash_info, k_iter,
323+
BTREE_ITER_with_updates);
324+
goto out;
325+
}
326+
327+
ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
328+
-BCH_ERR_transaction_restart_commit;
333329
}
330+
out:
334331
fsck_err:
335-
goto out;
336-
duplicate_entries:
337-
ret = hash_pick_winner(trans, *desc, hash_info, hash_k, k);
338-
if (ret < 0)
339-
goto out;
332+
bch2_trans_iter_exit(trans, dup_iter);
333+
printbuf_exit(&buf);
334+
if (free_snapshots_seen)
335+
darray_exit(&s->ids);
336+
return ret;
337+
}
340338

341-
if (!fsck_err(trans, hash_table_key_duplicate,
342-
"duplicate hash table keys%s:\n%s",
343-
ret != 2 ? "" : ", both point to valid inodes",
344-
(printbuf_reset(&buf),
345-
bch2_bkey_val_to_text(&buf, c, hash_k),
346-
prt_newline(&buf),
347-
bch2_bkey_val_to_text(&buf, c, k),
348-
buf.buf)))
349-
goto out;
339+
int __bch2_str_hash_check_key(struct btree_trans *trans,
340+
struct snapshots_seen *s,
341+
const struct bch_hash_desc *desc,
342+
struct bch_hash_info *hash_info,
343+
struct btree_iter *k_iter, struct bkey_s_c hash_k,
344+
bool *updated_before_k_pos)
345+
{
346+
struct bch_fs *c = trans->c;
347+
struct btree_iter iter = {};
348+
struct printbuf buf = PRINTBUF;
349+
struct bkey_s_c k;
350+
int ret = 0;
350351

351-
switch (ret) {
352-
case 0:
353-
ret = bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0);
354-
break;
355-
case 1:
356-
ret = bch2_hash_delete_at(trans, *desc, hash_info, &iter, 0);
357-
break;
358-
case 2:
359-
ret = fsck_rename_dirent(trans, s, *desc, hash_info, bkey_s_c_to_dirent(hash_k),
360-
updated_before_k_pos) ?:
361-
bch2_hash_delete_at(trans, *desc, hash_info, k_iter, 0);
362-
goto out;
352+
u64 hash = desc->hash_bkey(hash_info, hash_k);
353+
if (hash_k.k->p.offset < hash)
354+
goto bad_hash;
355+
356+
for_each_btree_key_norestart(trans, iter, desc->btree_id,
357+
SPOS(hash_k.k->p.inode, hash, hash_k.k->p.snapshot),
358+
BTREE_ITER_slots|
359+
BTREE_ITER_with_updates, k, ret) {
360+
if (bkey_eq(k.k->p, hash_k.k->p))
361+
break;
362+
363+
if (k.k->type == desc->key_type &&
364+
!desc->cmp_bkey(k, hash_k)) {
365+
ret = check_inode_hash_info_matches_root(trans, hash_k.k->p.inode,
366+
hash_info) ?:
367+
bch2_str_hash_repair_key(trans, s, desc, hash_info,
368+
k_iter, hash_k,
369+
&iter, k, updated_before_k_pos);
370+
break;
371+
}
372+
373+
if (bkey_deleted(k.k))
374+
goto bad_hash;
363375
}
376+
bch2_trans_iter_exit(trans, &iter);
377+
out:
378+
fsck_err:
379+
printbuf_exit(&buf);
380+
return ret;
381+
bad_hash:
382+
bch2_trans_iter_exit(trans, &iter);
383+
/*
384+
* Before doing any repair, check hash_info itself:
385+
*/
386+
ret = check_inode_hash_info_matches_root(trans, hash_k.k->p.inode, hash_info);
387+
if (ret)
388+
goto out;
364389

365-
ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
366-
-BCH_ERR_transaction_restart_commit;
390+
if (fsck_err(trans, hash_table_key_wrong_offset,
391+
"hash table key at wrong offset: should be at %llu\n%s",
392+
hash,
393+
(bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf)))
394+
ret = bch2_str_hash_repair_key(trans, s, desc, hash_info,
395+
k_iter, hash_k,
396+
&iter, bkey_s_c_null,
397+
updated_before_k_pos);
367398
goto out;
368399
}

fs/bcachefs/str_hash.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,14 @@ int bch2_hash_delete(struct btree_trans *trans,
398398
int bch2_repair_inode_hash_info(struct btree_trans *, struct bch_inode_unpacked *);
399399

400400
struct snapshots_seen;
401+
int bch2_str_hash_repair_key(struct btree_trans *,
402+
struct snapshots_seen *,
403+
const struct bch_hash_desc *,
404+
struct bch_hash_info *,
405+
struct btree_iter *, struct bkey_s_c,
406+
struct btree_iter *, struct bkey_s_c,
407+
bool *);
408+
401409
int __bch2_str_hash_check_key(struct btree_trans *,
402410
struct snapshots_seen *,
403411
const struct bch_hash_desc *,

0 commit comments

Comments
 (0)