@@ -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 :
334331fsck_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}
0 commit comments