|
14 | 14 | * Boston, MA 021110-1307, USA.
|
15 | 15 | */
|
16 | 16 |
|
| 17 | +#include <ctype.h> |
17 | 18 | #include "kerncompat.h"
|
18 | 19 | #include "kernel-shared/disk-io.h"
|
19 | 20 | #include "kernel-shared/ctree.h"
|
@@ -45,6 +46,21 @@ struct corrupted_block {
|
45 | 46 | unsigned long *error_mirror_bitmap;
|
46 | 47 | };
|
47 | 48 |
|
| 49 | +enum fix_data_checksum_action_value { |
| 50 | + ACTION_IGNORE, |
| 51 | + ACTION_LAST, |
| 52 | +}; |
| 53 | + |
| 54 | +static const struct fix_data_checksum_action { |
| 55 | + enum fix_data_checksum_action_value value; |
| 56 | + const char *string; |
| 57 | +} actions[] = { |
| 58 | + [ACTION_IGNORE] = { |
| 59 | + .value = ACTION_IGNORE, |
| 60 | + .string = "ignore", |
| 61 | + }, |
| 62 | +}; |
| 63 | + |
48 | 64 | static int global_repair_mode;
|
49 | 65 | LIST_HEAD(corrupted_blocks);
|
50 | 66 |
|
@@ -241,10 +257,49 @@ static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *c
|
241 | 257 | return ret;
|
242 | 258 | }
|
243 | 259 |
|
244 |
| -static void report_corrupted_blocks(struct btrfs_fs_info *fs_info) |
| 260 | +#define ASK_ACTION_BUFSIZE (32) |
| 261 | +static enum fix_data_checksum_action_value ask_action() |
| 262 | +{ |
| 263 | + char buf[ASK_ACTION_BUFSIZE] = { 0 }; |
| 264 | + bool printed; |
| 265 | + |
| 266 | +again: |
| 267 | + printed = false; |
| 268 | + for (int i = 0; i < ACTION_LAST; i++) { |
| 269 | + if (printed) |
| 270 | + printf("/"); |
| 271 | + /* Mark Ignore as default */ |
| 272 | + if (i == ACTION_IGNORE) |
| 273 | + printf("<<%c>>%s", toupper(actions[i].string[0]), |
| 274 | + actions[i].string + 1); |
| 275 | + else |
| 276 | + printf("<%c>%s", toupper(actions[i].string[0]), |
| 277 | + actions[i].string + 1); |
| 278 | + } |
| 279 | + printf(":"); |
| 280 | + fflush(stdout); |
| 281 | + /* Default to Ignore if no action provided. */ |
| 282 | + if (!fgets(buf, sizeof(buf) - 1, stdin)) |
| 283 | + return ACTION_IGNORE; |
| 284 | + if (buf[0] == '\n') |
| 285 | + return ACTION_IGNORE; |
| 286 | + /* Check exact match or matching the initial letter. */ |
| 287 | + 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) |
| 290 | + return actions[i].value; |
| 291 | + } |
| 292 | + /* No valid action found, retry. */ |
| 293 | + warning("invalid action, please retry"); |
| 294 | + goto again; |
| 295 | +} |
| 296 | + |
| 297 | +static void report_corrupted_blocks(struct btrfs_fs_info *fs_info, |
| 298 | + enum btrfs_fix_data_checksum_mode mode) |
245 | 299 | {
|
246 | 300 | struct corrupted_block *entry;
|
247 | 301 | struct btrfs_path path = { 0 };
|
| 302 | + enum fix_data_checksum_action_value action; |
248 | 303 |
|
249 | 304 | if (list_empty(&corrupted_blocks)) {
|
250 | 305 | printf("No data checksum mismatch found\n");
|
@@ -277,6 +332,16 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info)
|
277 | 332 | error("failed to iterate involved files: %m");
|
278 | 333 | break;
|
279 | 334 | }
|
| 335 | + switch (mode) { |
| 336 | + case BTRFS_FIX_DATA_CSUMS_INTERACTIVE: |
| 337 | + action = ask_action(); |
| 338 | + UASSERT(action == ACTION_IGNORE); |
| 339 | + fallthrough; |
| 340 | + case BTRFS_FIX_DATA_CSUMS_READONLY: |
| 341 | + break; |
| 342 | + default: |
| 343 | + UASSERT(0); |
| 344 | + } |
280 | 345 | }
|
281 | 346 | }
|
282 | 347 |
|
@@ -333,7 +398,7 @@ int btrfs_recover_fix_data_checksum(const char *path,
|
333 | 398 | errno = -ret;
|
334 | 399 | error("failed to iterate csum tree: %m");
|
335 | 400 | }
|
336 |
| - report_corrupted_blocks(fs_info); |
| 401 | + report_corrupted_blocks(fs_info, mode); |
337 | 402 | out_close:
|
338 | 403 | free_corrupted_blocks();
|
339 | 404 | close_ctree_fs_info(fs_info);
|
|
0 commit comments