Skip to content

Commit e3b9a9d

Browse files
adam900710kdave
authored andcommitted
btrfs-progs: fix-data-checksum: update csum items to fix csum mismatch
Add a new group of actions in the interactive mode to fix a csum mismatch. The output looks like this: logical=13631488 corrtuped mirrors=1 affected files: (subvolume 5)/foo (subvolume 5)/dir/bar <<I>>gnore/<1>:1 Csum item for logical 13631488 updated using data from mirror 1 In the interactive mode, the update-csum-item behavior is printed as all available mirror numbers. Considering all the existing (and future) actions, the name should start with an single letter, it's pretty easy to distinguish mirror number from other actions. The update-csum-item action itself is pretty straight-forward, just read out the data from specified mirror, then calculate a new checksum, and update the corresponding csum item in csum tree. Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent e99cae2 commit e3b9a9d

File tree

3 files changed

+113
-12
lines changed

3 files changed

+113
-12
lines changed

cmds/rescue-fix-data-checksum.c

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
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

4951
enum 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

6471
static 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

268278
again:
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

299384
static 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);

kernel-shared/file-item.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans,
112112
return err;
113113
}
114114

115-
static struct btrfs_csum_item *
115+
struct btrfs_csum_item *
116116
btrfs_lookup_csum(struct btrfs_trans_handle *trans,
117117
struct btrfs_root *root,
118118
struct btrfs_path *path,

kernel-shared/file-item.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
8989
struct btrfs_file_extent_item *stack_fi);
9090
int btrfs_csum_file_block(struct btrfs_trans_handle *trans, u64 logical,
9191
u64 csum_objectid, u32 csum_type, const char *data);
92+
struct btrfs_csum_item *
93+
btrfs_lookup_csum(struct btrfs_trans_handle *trans,
94+
struct btrfs_root *root,
95+
struct btrfs_path *path,
96+
u64 bytenr, u64 csum_objectid, u16 csum_type, int cow);
9297
int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans,
9398
struct btrfs_root *root, u64 objectid,
9499
u64 offset, const char *buffer, size_t size,

0 commit comments

Comments
 (0)