20
20
#include "kernel-shared/ctree.h"
21
21
#include "kernel-shared/volumes.h"
22
22
#include "kernel-shared/backref.h"
23
+ #include "kernel-shared/transaction.h"
24
+ #include "kernel-shared/file-item.h"
23
25
#include "common/messages.h"
24
26
#include "common/open-utils.h"
25
27
#include "cmds/rescue.h"
@@ -48,6 +50,7 @@ struct corrupted_block {
48
50
49
51
enum fix_data_checksum_action_value {
50
52
ACTION_IGNORE ,
53
+ ACTION_UPDATE_CSUM ,
51
54
ACTION_LAST ,
52
55
};
53
56
@@ -59,6 +62,10 @@ static const struct fix_data_checksum_action {
59
62
.value = ACTION_IGNORE ,
60
63
.string = "ignore" ,
61
64
},
65
+ [ACTION_UPDATE_CSUM ] = {
66
+ .value = ACTION_UPDATE_CSUM ,
67
+ .string = "update-csum" ,
68
+ },
62
69
};
63
70
64
71
static int global_repair_mode ;
@@ -258,23 +265,36 @@ static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *c
258
265
}
259
266
260
267
#define ASK_ACTION_BUFSIZE (32)
261
- static enum fix_data_checksum_action_value ask_action ()
268
+ static enum fix_data_checksum_action_value ask_action (unsigned int num_mirrors ,
269
+ unsigned int * mirror_ret )
262
270
{
271
+ unsigned long ret ;
263
272
char buf [ASK_ACTION_BUFSIZE ] = { 0 };
264
273
bool printed ;
274
+ char * endptr ;
265
275
266
276
again :
267
277
printed = false;
268
278
for (int i = 0 ; i < ACTION_LAST ; i ++ ) {
269
279
if (printed )
270
280
printf ("/" );
271
281
/* Mark Ignore as default */
272
- if (i == ACTION_IGNORE )
282
+ if (i == ACTION_IGNORE ) {
273
283
printf ("<<%c>>%s" , toupper (actions [i ].string [0 ]),
274
284
actions [i ].string + 1 );
275
- else
285
+ } else if (i == ACTION_UPDATE_CSUM ) {
286
+ /*
287
+ * For update-csum action, we need a mirror number,
288
+ * so output all valid mirrors numbers instead.
289
+ */
290
+ for (int cur_mirror = 1 ; cur_mirror <= num_mirrors ;
291
+ cur_mirror ++ )
292
+ printf ("<%u>" , cur_mirror );
293
+ } else {
276
294
printf ("<%c>%s" , toupper (actions [i ].string [0 ]),
277
295
actions [i ].string + 1 );
296
+ }
297
+ printed = true;
278
298
}
279
299
printf (":" );
280
300
fflush (stdout );
@@ -285,13 +305,79 @@ static enum fix_data_checksum_action_value ask_action()
285
305
return ACTION_IGNORE ;
286
306
/* Check exact match or matching the initial letter. */
287
307
for (int i = 0 ; i < ACTION_LAST ; i ++ ) {
288
- if (strncasecmp (buf , actions [i ].string , 1 ) == 0 ||
289
- strncasecmp (buf , actions [i ].string , ASK_ACTION_BUFSIZE ) == 0 )
308
+ if ((strncasecmp (buf , actions [i ].string , 1 ) == 0 ||
309
+ strncasecmp (buf , actions [i ].string , ASK_ACTION_BUFSIZE ) == 0 ) &&
310
+ actions [i ].value != ACTION_UPDATE_CSUM )
290
311
return actions [i ].value ;
291
312
}
292
- /* No valid action found, retry. */
293
- warning ("invalid action, please retry" );
294
- goto again ;
313
+ /* No match, check if it's some numeric string. */
314
+ ret = strtoul (buf , & endptr , 10 );
315
+ if (endptr == buf || ret == ULONG_MAX ) {
316
+ /* No valid action found, retry. */
317
+ warning ("invalid action, please retry" );
318
+ goto again ;
319
+ }
320
+ if (ret > num_mirrors || ret == 0 ) {
321
+ warning ("invalid mirror number %lu, must be in range [1, %d], please retry" ,
322
+ ret , num_mirrors );
323
+ goto again ;
324
+ }
325
+ * mirror_ret = ret ;
326
+ return ACTION_UPDATE_CSUM ;
327
+ }
328
+
329
+ static int update_csum_item (struct btrfs_fs_info * fs_info , u64 logical ,
330
+ unsigned int mirror )
331
+ {
332
+ struct btrfs_trans_handle * trans ;
333
+ struct btrfs_root * csum_root = btrfs_csum_root (fs_info , logical );
334
+ struct btrfs_path path = { 0 };
335
+ struct btrfs_csum_item * citem ;
336
+ u64 read_len = fs_info -> sectorsize ;
337
+ u8 csum [BTRFS_CSUM_SIZE ] = { 0 };
338
+ u8 * buf ;
339
+ int ret ;
340
+
341
+ buf = malloc (fs_info -> sectorsize );
342
+ if (!buf )
343
+ return - ENOMEM ;
344
+ ret = read_data_from_disk (fs_info , buf , logical , & read_len , mirror );
345
+ if (ret < 0 ) {
346
+ errno = - ret ;
347
+ error ("failed to read block at logical %llu mirror %u: %m" ,
348
+ logical , mirror );
349
+ goto out ;
350
+ }
351
+ trans = btrfs_start_transaction (csum_root , 1 );
352
+ if (IS_ERR (trans )) {
353
+ ret = PTR_ERR (trans );
354
+ errno = - ret ;
355
+ error_msg (ERROR_MSG_START_TRANS , "%m" );
356
+ goto out ;
357
+ }
358
+ citem = btrfs_lookup_csum (trans , csum_root , & path , logical ,
359
+ BTRFS_EXTENT_CSUM_OBJECTID , fs_info -> csum_type , 1 );
360
+ if (IS_ERR (citem )) {
361
+ ret = PTR_ERR (citem );
362
+ errno = - ret ;
363
+ error ("failed to find csum item for logical %llu: $m" , logical );
364
+ btrfs_abort_transaction (trans , ret );
365
+ goto out ;
366
+ }
367
+ btrfs_csum_data (fs_info , fs_info -> csum_type , buf , csum , fs_info -> sectorsize );
368
+ write_extent_buffer (path .nodes [0 ], csum , (unsigned long )citem , fs_info -> csum_size );
369
+ btrfs_release_path (& path );
370
+ ret = btrfs_commit_transaction (trans , csum_root );
371
+ if (ret < 0 ) {
372
+ errno = - ret ;
373
+ error_msg (ERROR_MSG_COMMIT_TRANS , "%m" );
374
+ }
375
+ printf ("Csum item for logical %llu updated using data from mirror %u\n" ,
376
+ logical , mirror );
377
+ out :
378
+ free (buf );
379
+ btrfs_release_path (& path );
380
+ return ret ;
295
381
}
296
382
297
383
static void report_corrupted_blocks (struct btrfs_fs_info * fs_info ,
@@ -307,6 +393,7 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info,
307
393
}
308
394
309
395
list_for_each_entry (entry , & corrupted_blocks , list ) {
396
+ unsigned int mirror ;
310
397
bool has_printed = false;
311
398
int ret ;
312
399
@@ -334,10 +421,20 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info,
334
421
}
335
422
switch (mode ) {
336
423
case BTRFS_FIX_DATA_CSUMS_INTERACTIVE :
337
- action = ask_action ();
338
- UASSERT (action == ACTION_IGNORE );
339
- fallthrough ;
424
+ action = ask_action (entry -> num_mirrors , & mirror );
425
+ break ;
340
426
case BTRFS_FIX_DATA_CSUMS_READONLY :
427
+ action = ACTION_IGNORE ;
428
+ break ;
429
+ default :
430
+ UASSERT (0 );
431
+ }
432
+
433
+ switch (action ) {
434
+ case ACTION_IGNORE :
435
+ break ;
436
+ case ACTION_UPDATE_CSUM :
437
+ ret = update_csum_item (fs_info , entry -> logical , mirror );
341
438
break ;
342
439
default :
343
440
UASSERT (0 );
0 commit comments