2020#include "kernel-shared/ctree.h"
2121#include "kernel-shared/volumes.h"
2222#include "kernel-shared/backref.h"
23+ #include "kernel-shared/transaction.h"
24+ #include "kernel-shared/file-item.h"
2325#include "common/messages.h"
2426#include "common/open-utils.h"
2527#include "cmds/rescue.h"
@@ -48,6 +50,7 @@ struct corrupted_block {
4850
4951enum fix_data_checksum_action_value {
5052 ACTION_IGNORE ,
53+ ACTION_UPDATE_CSUM ,
5154 ACTION_LAST ,
5255};
5356
@@ -59,6 +62,10 @@ static const struct fix_data_checksum_action {
5962 .value = ACTION_IGNORE ,
6063 .string = "ignore"
6164 },
65+ [ACTION_UPDATE_CSUM ] = {
66+ .value = ACTION_UPDATE_CSUM ,
67+ .string = "update-csum"
68+ },
6269};
6370
6471static int global_repair_mode ;
@@ -260,23 +267,35 @@ static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *c
260267}
261268
262269#define ASK_ACTION_BUFSIZE (32)
263- static enum fix_data_checksum_action_value ask_action (void )
270+ static enum fix_data_checksum_action_value ask_action (unsigned int num_mirrors ,
271+ unsigned int * mirror_ret )
264272{
273+ unsigned long ret ;
265274 char buf [ASK_ACTION_BUFSIZE ] = { 0 };
266275 bool printed ;
276+ char * endptr ;
267277
268278again :
269279 printed = false;
270280 for (int i = 0 ; i < ACTION_LAST ; i ++ ) {
271281 if (printed )
272282 pr_verbose (LOG_DEFAULT , "/" );
273283 /* Mark Ignore as default. */
274- if (i == ACTION_IGNORE )
284+ if (i == ACTION_IGNORE ) {
275285 pr_verbose (LOG_DEFAULT , "<<%c>>%s" , toupper (actions [i ].string [0 ]),
276286 actions [i ].string + 1 );
277- else
287+ } else if (i == ACTION_UPDATE_CSUM ) {
288+ /*
289+ * For update-csum action, we need a mirror number,
290+ * so output all valid mirrors numbers instead.
291+ */
292+ for (int cur_mirror = 1 ; cur_mirror <= num_mirrors ; cur_mirror ++ )
293+ pr_verbose (LOG_DEFAULT , "<%u>" , cur_mirror );
294+ } else {
278295 pr_verbose (LOG_DEFAULT , "<%c>%s" , toupper (actions [i ].string [0 ]),
279296 actions [i ].string + 1 );
297+ }
298+ printed = true;
280299 }
281300 pr_verbose (LOG_DEFAULT , ":" );
282301 fflush (stdout );
@@ -287,13 +306,79 @@ static enum fix_data_checksum_action_value ask_action(void)
287306 return ACTION_IGNORE ;
288307 /* Check exact match or matching the initial letter. */
289308 for (int i = 0 ; i < ACTION_LAST ; i ++ ) {
290- if (strncasecmp (buf , actions [i ].string , 1 ) == 0 ||
291- strncasecmp (buf , actions [i ].string , ASK_ACTION_BUFSIZE ) == 0 )
309+ if ((strncasecmp (buf , actions [i ].string , 1 ) == 0 ||
310+ strncasecmp (buf , actions [i ].string , ASK_ACTION_BUFSIZE ) == 0 ) &&
311+ actions [i ].value != ACTION_UPDATE_CSUM )
292312 return actions [i ].value ;
293313 }
294- /* No valid action found, retry. */
295- warning ("invalid action, please retry" );
296- goto again ;
314+ /* No match, check if it's some numeric string. */
315+ ret = strtoul (buf , & endptr , 10 );
316+ if (endptr == buf || ret == ULONG_MAX ) {
317+ /* No valid action found, retry. */
318+ warning ("invalid action, please retry" );
319+ goto again ;
320+ }
321+ if (ret > num_mirrors || ret == 0 ) {
322+ warning ("invalid mirror number %lu, must be in range [1, %d], please retry" ,
323+ ret , num_mirrors );
324+ goto again ;
325+ }
326+ * mirror_ret = ret ;
327+ return ACTION_UPDATE_CSUM ;
328+ }
329+
330+ static int update_csum_item (struct btrfs_fs_info * fs_info , u64 logical ,
331+ unsigned int mirror )
332+ {
333+ struct btrfs_trans_handle * trans ;
334+ struct btrfs_root * csum_root = btrfs_csum_root (fs_info , logical );
335+ struct btrfs_path path = { 0 };
336+ struct btrfs_csum_item * citem ;
337+ u64 read_len = fs_info -> sectorsize ;
338+ u8 csum [BTRFS_CSUM_SIZE ] = { 0 };
339+ u8 * buf ;
340+ int ret ;
341+
342+ buf = malloc (fs_info -> sectorsize );
343+ if (!buf )
344+ return - ENOMEM ;
345+ ret = read_data_from_disk (fs_info , buf , logical , & read_len , mirror );
346+ if (ret < 0 ) {
347+ errno = - ret ;
348+ error ("failed to read block at logical %llu mirror %u: %m" ,
349+ logical , mirror );
350+ goto out ;
351+ }
352+ trans = btrfs_start_transaction (csum_root , 1 );
353+ if (IS_ERR (trans )) {
354+ ret = PTR_ERR (trans );
355+ errno = - ret ;
356+ error_msg (ERROR_MSG_START_TRANS , "%m" );
357+ goto out ;
358+ }
359+ citem = btrfs_lookup_csum (trans , csum_root , & path , logical ,
360+ BTRFS_EXTENT_CSUM_OBJECTID , fs_info -> csum_type , 1 );
361+ if (IS_ERR (citem )) {
362+ ret = PTR_ERR (citem );
363+ errno = - ret ;
364+ error ("failed to find csum item for logical %llu: $m" , logical );
365+ btrfs_abort_transaction (trans , ret );
366+ goto out ;
367+ }
368+ btrfs_csum_data (fs_info , fs_info -> csum_type , buf , csum , fs_info -> sectorsize );
369+ write_extent_buffer (path .nodes [0 ], csum , (unsigned long )citem , fs_info -> csum_size );
370+ btrfs_release_path (& path );
371+ ret = btrfs_commit_transaction (trans , csum_root );
372+ if (ret < 0 ) {
373+ errno = - ret ;
374+ error_msg (ERROR_MSG_COMMIT_TRANS , "%m" );
375+ }
376+ printf ("Csum item for logical %llu updated using data from mirror %u\n" ,
377+ logical , mirror );
378+ out :
379+ free (buf );
380+ btrfs_release_path (& path );
381+ return ret ;
297382}
298383
299384static void report_corrupted_blocks (struct btrfs_fs_info * fs_info ,
@@ -309,6 +394,7 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info,
309394 }
310395
311396 list_for_each_entry (entry , & corrupted_blocks , list ) {
397+ unsigned int mirror ;
312398 bool has_printed = false;
313399 int ret ;
314400
@@ -336,10 +422,20 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info,
336422 }
337423 switch (mode ) {
338424 case BTRFS_FIX_DATA_CSUMS_INTERACTIVE :
339- action = ask_action ();
340- UASSERT (action == ACTION_IGNORE );
341- fallthrough ;
425+ action = ask_action (entry -> num_mirrors , & mirror );
426+ break ;
342427 case BTRFS_FIX_DATA_CSUMS_READONLY :
428+ action = ACTION_IGNORE ;
429+ break ;
430+ default :
431+ UASSERT (0 );
432+ }
433+
434+ switch (action ) {
435+ case ACTION_IGNORE :
436+ break ;
437+ case ACTION_UPDATE_CSUM :
438+ ret = update_csum_item (fs_info , entry -> logical , mirror );
343439 break ;
344440 default :
345441 UASSERT (0 );
0 commit comments