Skip to content

Commit 535b321

Browse files
committed
btrfs-progs: fix-data-checksum: update csum items to fix csum mismatch
This adds a new group of action 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 outputted as all available mirror numbers. Considering all the existing (and future) action should starts with an alphabet, 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 <[email protected]>
1 parent c58de57 commit 535b321

File tree

3 files changed

+114
-12
lines changed

3 files changed

+114
-12
lines changed

cmds/rescue-fix-data-checksum.c

Lines changed: 108 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;
@@ -258,23 +265,36 @@ static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *c
258265
}
259266

260267
#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)
262270
{
271+
unsigned long ret;
263272
char buf[ASK_ACTION_BUFSIZE] = { 0 };
264273
bool printed;
274+
char *endptr;
265275

266276
again:
267277
printed = false;
268278
for (int i = 0; i < ACTION_LAST; i++) {
269279
if (printed)
270280
printf("/");
271281
/* Mark Ignore as default */
272-
if (i == ACTION_IGNORE)
282+
if (i == ACTION_IGNORE) {
273283
printf("<<%c>>%s", toupper(actions[i].string[0]),
274284
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 {
276294
printf("<%c>%s", toupper(actions[i].string[0]),
277295
actions[i].string + 1);
296+
}
297+
printed = true;
278298
}
279299
printf(":");
280300
fflush(stdout);
@@ -285,13 +305,79 @@ static enum fix_data_checksum_action_value ask_action()
285305
return ACTION_IGNORE;
286306
/* Check exact match or matching the initial letter. */
287307
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)
290311
return actions[i].value;
291312
}
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;
295381
}
296382

297383
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,
307393
}
308394

309395
list_for_each_entry(entry, &corrupted_blocks, list) {
396+
unsigned int mirror;
310397
bool has_printed = false;
311398
int ret;
312399

@@ -334,10 +421,20 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info,
334421
}
335422
switch (mode) {
336423
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;
340426
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);
341438
break;
342439
default:
343440
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)