diff options
Diffstat (limited to 'check/mode-lowmem.c')
-rw-r--r-- | check/mode-lowmem.c | 74 |
1 files changed, 73 insertions, 1 deletions
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index 0b61526f..9e845832 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -1735,6 +1735,71 @@ static int punch_extent_hole(struct btrfs_root *root, u64 ino, u64 start, return ret; } +static int repair_inline_ram_bytes(struct btrfs_root *root, + struct btrfs_path *path, u64 *ram_bytes_ret) +{ + struct btrfs_trans_handle *trans; + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + struct btrfs_item *item; + u32 on_disk_data_len; + int ret; + int recover_ret; + + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + return ret; + } + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_release_path(path); + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + /* Not really possible */ + if (ret > 0) { + ret = -ENOENT; + btrfs_release_path(path); + goto recover; + } + if (ret < 0) + goto recover; + + item = btrfs_item_nr(path->slots[0]); + on_disk_data_len = btrfs_file_extent_inline_item_len(path->nodes[0], + item); + + fi = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(path->nodes[0], fi) != + BTRFS_FILE_EXTENT_INLINE || + btrfs_file_extent_compression(path->nodes[0], fi) != + BTRFS_COMPRESS_NONE) + return -EINVAL; + btrfs_set_file_extent_ram_bytes(path->nodes[0], fi, on_disk_data_len); + btrfs_mark_buffer_dirty(path->nodes[0]); + + ret = btrfs_commit_transaction(trans, root); + if (!ret) { + printf( + "Successfully repaired inline ram_bytes for root %llu ino %llu\n", + root->objectid, key.objectid); + *ram_bytes_ret = on_disk_data_len; + } + return ret; + +recover: + /* + * COW search failed, mostly due to the extra COW work (extent + * allocation, etc). Since we have a good path from before, readonly + * search should still work, or later checks will fail due to empty + * path. + */ + recover_ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + + /* This really shouldn't happen, or we have a big problem */ + ASSERT(recover_ret == 0); + return ret; +} + /* * Check file extent datasum/hole, update the size of the file extents, * check and update the last offset of the file extent. @@ -1817,7 +1882,14 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_path *path, "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u", root->objectid, fkey.objectid, fkey.offset, extent_num_bytes, item_inline_len); - err |= FILE_EXTENT_ERROR; + if (repair) { + ret = repair_inline_ram_bytes(root, path, + &extent_num_bytes); + if (ret) + err |= FILE_EXTENT_ERROR; + } else { + err |= FILE_EXTENT_ERROR; + } } *end += extent_num_bytes; *size += extent_num_bytes; |