Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
btrfs: Fix logical to physical block address mapping
The current btrfs support did not handled multiple stripes stored in
chunk items, hence skipping the physical addresses that were needed to
do the mapping.

Besides, the chunk tree may contain DEV_ITEM keys which store
information on all of the underlying block devices, so we must skip them
instead of finishing lookup.

The bug was reproduced with btrfs-progs v4.2.2.

Cc: Gene Cumm <gene.cumm@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
 * Do not set ignore_key multiple times. Set it before parsing chunk
   tree.
v2 -> v3:
 * Replace an unnecessary goto with a continue statement.
  • Loading branch information
Paulo Alcantara committed Dec 27, 2015
1 parent 721a0af commit 5483860
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 16 deletions.
49 changes: 33 additions & 16 deletions core/fs/btrfs/btrfs.c
Expand Up @@ -81,7 +81,8 @@ static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item *m1,
}

/* insert a new chunk mapping item */
static void insert_map(struct fs_info *fs, struct btrfs_chunk_map_item *item)
static void insert_chunk_item(struct fs_info *fs,
struct btrfs_chunk_map_item *item)
{
struct btrfs_info * const bfs = fs->fs_info;
struct btrfs_chunk_map *chunk_map = &bfs->chunk_map;
Expand Down Expand Up @@ -113,6 +114,22 @@ static void insert_map(struct fs_info *fs, struct btrfs_chunk_map_item *item)
chunk_map->cur_length++;
}

static inline void insert_map(struct fs_info *fs, struct btrfs_disk_key *key,
struct btrfs_chunk *chunk)
{
struct btrfs_stripe *stripe = &chunk->stripe;
struct btrfs_stripe *stripe_end = stripe + chunk->num_stripes;
struct btrfs_chunk_map_item item;

item.logical = key->offset;
item.length = chunk->length;
for ( ; stripe < stripe_end; stripe++) {
item.devid = stripe->devid;
item.physical = stripe->offset;
insert_chunk_item(fs, &item);
}
}

/*
* from sys_chunk_array or chunk_tree, we can convert a logical address to
* a physical address we can not support multi device case yet
Expand Down Expand Up @@ -330,7 +347,6 @@ static int next_slot(struct fs_info *fs, struct btrfs_disk_key *key,
static void btrfs_read_sys_chunk_array(struct fs_info *fs)
{
struct btrfs_info * const bfs = fs->fs_info;
struct btrfs_chunk_map_item item;
struct btrfs_disk_key *key;
struct btrfs_chunk *chunk;
int cur;
Expand All @@ -342,27 +358,26 @@ static void btrfs_read_sys_chunk_array(struct fs_info *fs)
cur += sizeof(*key);
chunk = (struct btrfs_chunk *)(bfs->sb.sys_chunk_array + cur);
cur += btrfs_chunk_item_size(chunk->num_stripes);
/* insert to mapping table, ignore multi stripes */
item.logical = key->offset;
item.length = chunk->length;
item.devid = chunk->stripe.devid;
item.physical = chunk->stripe.offset;/*ignore other stripes */
insert_map(fs, &item);
insert_map(fs, key, chunk);
}
}

/* read chunk items from chunk_tree and insert them to chunk map */
static void btrfs_read_chunk_tree(struct fs_info *fs)
{
struct btrfs_info * const bfs = fs->fs_info;
struct btrfs_disk_key ignore_key;
struct btrfs_disk_key search_key;
struct btrfs_chunk *chunk;
struct btrfs_chunk_map_item item;
struct btrfs_path path;

if (!(bfs->sb.flags & BTRFS_SUPER_FLAG_METADUMP)) {
if (bfs->sb.num_devices > 1)
printf("warning: only support single device btrfs\n");

ignore_key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
ignore_key.type = BTRFS_DEV_ITEM_KEY;

/* read chunk from chunk_tree */
search_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
search_key.type = BTRFS_CHUNK_ITEM_KEY;
Expand All @@ -371,16 +386,18 @@ static void btrfs_read_chunk_tree(struct fs_info *fs)
search_tree(fs, bfs->sb.chunk_root, &search_key, &path);
do {
do {
/* skip information about underlying block
* devices.
*/
if (!btrfs_comp_keys_type(&ignore_key,
&path.item.key))
continue;
if (btrfs_comp_keys_type(&search_key,
&path.item.key))
&path.item.key))
break;

chunk = (struct btrfs_chunk *)(path.data);
/* insert to mapping table, ignore stripes */
item.logical = path.item.key.offset;
item.length = chunk->length;
item.devid = chunk->stripe.devid;
item.physical = chunk->stripe.offset;
insert_map(fs, &item);
insert_map(fs, &path.item.key, chunk);
} while (!next_slot(fs, &search_key, &path));
if (btrfs_comp_keys_type(&search_key, &path.item.key))
break;
Expand Down
2 changes: 2 additions & 0 deletions core/fs/btrfs/btrfs.h
Expand Up @@ -56,6 +56,8 @@ typedef u64 __le64;
#define BTRFS_MAX_LEVEL 8
#define BTRFS_MAX_CHUNK_ENTRIES 256

#define BTRFS_DEV_ITEMS_OBJECTID 1ULL

#define BTRFS_FT_REG_FILE 1
#define BTRFS_FT_DIR 2
#define BTRFS_FT_SYMLINK 7
Expand Down

0 comments on commit 5483860

Please sign in to comment.