summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds-check.c45
-rw-r--r--tests/fsck-tests/005-bad-item-offset.imgbin0 -> 398336 bytes
2 files changed, 45 insertions, 0 deletions
diff --git a/cmds-check.c b/cmds-check.c
index db7f7452..acdf312f 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -2834,6 +2834,42 @@ static int fix_key_order(struct btrfs_trans_handle *trans,
return ret;
}
+static int delete_bogus_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct extent_buffer *buf, int slot)
+{
+ struct btrfs_key key;
+ int nritems = btrfs_header_nritems(buf);
+
+ btrfs_item_key_to_cpu(buf, &key, slot);
+
+ /* These are all the keys we can deal with missing. */
+ if (key.type != BTRFS_DIR_INDEX_KEY &&
+ key.type != BTRFS_EXTENT_ITEM_KEY &&
+ key.type != BTRFS_METADATA_ITEM_KEY &&
+ key.type != BTRFS_TREE_BLOCK_REF_KEY &&
+ key.type != BTRFS_EXTENT_DATA_REF_KEY)
+ return -1;
+
+ printf("Deleting bogus item [%llu,%u,%llu] at slot %d on block %llu\n",
+ (unsigned long long)key.objectid, key.type,
+ (unsigned long long)key.offset, slot, buf->start);
+ memmove_extent_buffer(buf, btrfs_item_nr_offset(slot),
+ btrfs_item_nr_offset(slot + 1),
+ sizeof(struct btrfs_item) *
+ (nritems - slot - 1));
+ btrfs_set_header_nritems(buf, nritems - 1);
+ if (slot == 0) {
+ struct btrfs_disk_key disk_key;
+
+ btrfs_item_key(buf, &disk_key, 0);
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
+ }
+ btrfs_mark_buffer_dirty(buf);
+ return 0;
+}
+
static int fix_item_offset(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf)
@@ -2873,6 +2909,7 @@ static int fix_item_offset(struct btrfs_trans_handle *trans,
}
buf = path->nodes[level];
+again:
for (i = 0; i < btrfs_header_nritems(buf); i++) {
unsigned int shift = 0, offset;
@@ -2880,6 +2917,10 @@ static int fix_item_offset(struct btrfs_trans_handle *trans,
BTRFS_LEAF_DATA_SIZE(root)) {
if (btrfs_item_end_nr(buf, i) >
BTRFS_LEAF_DATA_SIZE(root)) {
+ ret = delete_bogus_item(trans, root, path,
+ buf, i);
+ if (!ret)
+ goto again;
fprintf(stderr, "item is off the end of the "
"leaf, can't fix\n");
ret = -EIO;
@@ -2891,6 +2932,10 @@ static int fix_item_offset(struct btrfs_trans_handle *trans,
btrfs_item_offset_nr(buf, i - 1)) {
if (btrfs_item_end_nr(buf, i) >
btrfs_item_offset_nr(buf, i - 1)) {
+ ret = delete_bogus_item(trans, root, path,
+ buf, i);
+ if (!ret)
+ goto again;
fprintf(stderr, "items overlap, can't fix\n");
ret = -EIO;
break;
diff --git a/tests/fsck-tests/005-bad-item-offset.img b/tests/fsck-tests/005-bad-item-offset.img
new file mode 100644
index 00000000..e11e1e32
--- /dev/null
+++ b/tests/fsck-tests/005-bad-item-offset.img
Binary files differ