fd = open(device->name, flags);
if (fd < 0) {
ret = -errno;
+ error("cannot open device '%s': %s", device->name,
+ strerror(errno));
goto fail;
}
u64 *total_devs, u64 super_offset, int super_recover)
{
struct btrfs_super_block *disk_super;
- char *buf;
+ char buf[BTRFS_SUPER_INFO_SIZE];
int ret;
u64 devid;
- buf = malloc(4096);
- if (!buf) {
- ret = -ENOMEM;
- goto error;
- }
disk_super = (struct btrfs_super_block *)buf;
ret = btrfs_read_dev_super(fd, disk_super, super_offset, super_recover);
- if (ret < 0) {
- ret = -EIO;
- goto error_brelse;
- }
+ if (ret < 0)
+ return -EIO;
devid = btrfs_stack_device_id(&disk_super->dev_item);
if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)
*total_devs = 1;
ret = device_list_add(path, disk_super, devid, fs_devices_ret);
-error_brelse:
- free(buf);
-error:
return ret;
}
struct btrfs_device *device,
u64 chunk_tree, u64 chunk_objectid,
u64 chunk_offset,
- u64 num_bytes, u64 *start)
+ u64 num_bytes, u64 *start, int convert)
{
int ret;
struct btrfs_path *path;
if (!path)
return -ENOMEM;
- ret = find_free_dev_extent(trans, device, path, num_bytes, start);
- if (ret) {
- goto err;
+ /*
+ * For convert case, just skip search free dev_extent, as caller
+ * is responsible to make sure it's free.
+ */
+ if (!convert) {
+ ret = find_free_dev_extent(trans, device, path, num_bytes,
+ start);
+ if (ret)
+ goto err;
}
key.objectid = device->devid;
ret = btrfs_alloc_dev_extent(trans, device,
info->chunk_root->root_key.objectid,
BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset,
- calc_size, &dev_offset);
+ calc_size, &dev_offset, 0);
BUG_ON(ret);
device->bytes_used += calc_size;
return ret;
}
+/*
+ * Alloc a DATA chunk with SINGLE profile.
+ *
+ * If 'convert' is set, it will alloc a chunk with 1:1 mapping
+ * (btrfs logical bytenr == on-disk bytenr)
+ * For that case, caller must make sure the chunk and dev_extent are not
+ * occupied.
+ */
int btrfs_alloc_data_chunk(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 *start,
- u64 num_bytes, u64 type)
+ u64 num_bytes, u64 type, int convert)
{
u64 dev_offset;
struct btrfs_fs_info *info = extent_root->fs_info;
key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
key.type = BTRFS_CHUNK_ITEM_KEY;
- ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- &key.offset);
- if (ret)
- return ret;
+ if (convert) {
+ BUG_ON(*start != round_down(*start, extent_root->sectorsize));
+ key.offset = *start;
+ dev_offset = *start;
+ } else {
+ ret = find_next_chunk(chunk_root,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+ &key.offset);
+ if (ret)
+ return ret;
+ }
chunk = kmalloc(btrfs_chunk_item_size(num_stripes), GFP_NOFS);
if (!chunk)
ret = btrfs_alloc_dev_extent(trans, device,
info->chunk_root->root_key.objectid,
BTRFS_FIRST_CHUNK_TREE_OBJECTID, key.offset,
- calc_size, &dev_offset);
+ calc_size, &dev_offset, convert);
BUG_ON(ret);
device->bytes_used += calc_size;
ret = btrfs_insert_item(trans, chunk_root, &key, chunk,
btrfs_chunk_item_size(num_stripes));
BUG_ON(ret);
- *start = key.offset;
+ if (!convert)
+ *start = key.offset;
map->ce.start = key.offset;
map->ce.size = num_bytes;
return ret;
}
-int btrfs_next_metadata(struct btrfs_mapping_tree *map_tree, u64 *logical,
- u64 *size)
+int btrfs_next_bg(struct btrfs_mapping_tree *map_tree, u64 *logical,
+ u64 *size, u64 type)
{
struct cache_extent *ce;
struct map_lookup *map;
+ u64 cur = *logical;
- ce = search_cache_extent(&map_tree->cache_tree, *logical);
+ ce = search_cache_extent(&map_tree->cache_tree, cur);
while (ce) {
- ce = next_cache_extent(ce);
- if (!ce)
- return -ENOENT;
+ /*
+ * only jump to next bg if our cur is not 0
+ * As the initial logical for btrfs_next_bg() is 0, and
+ * if we jump to next bg, we skipped a valid bg.
+ */
+ if (cur) {
+ ce = next_cache_extent(ce);
+ if (!ce)
+ return -ENOENT;
+ }
+ cur = ce->start;
map = container_of(ce, struct map_lookup, ce);
- if (map->type & BTRFS_BLOCK_GROUP_METADATA) {
+ if (map->type & type) {
*logical = ce->start;
*size = ce->size;
return 0;
}
/*
- * Slot is used to verfy the chunk item is valid
+ * slot == -1: SYSTEM chunk
+ * return -EIO on error, otherwise return 0
+ */
+static int btrfs_check_chunk_valid(struct btrfs_root *root,
+ struct extent_buffer *leaf,
+ struct btrfs_chunk *chunk,
+ int slot, u64 logical)
+{
+ u64 length;
+ u64 stripe_len;
+ u16 num_stripes;
+ u16 sub_stripes;
+ u64 type;
+
+ length = btrfs_chunk_length(leaf, chunk);
+ stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
+ type = btrfs_chunk_type(leaf, chunk);
+
+ /*
+ * These valid checks may be insufficient to cover every corner cases.
+ */
+ if (!IS_ALIGNED(logical, root->sectorsize)) {
+ error("invalid chunk logical %llu", logical);
+ return -EIO;
+ }
+ if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
+ error("invalid chunk sectorsize %llu",
+ (unsigned long long)btrfs_chunk_sector_size(leaf, chunk));
+ return -EIO;
+ }
+ if (!length || !IS_ALIGNED(length, root->sectorsize)) {
+ error("invalid chunk length %llu", length);
+ return -EIO;
+ }
+ if (stripe_len != BTRFS_STRIPE_LEN) {
+ error("invalid chunk stripe length: %llu", stripe_len);
+ return -EIO;
+ }
+ /* Check on chunk item type */
+ if (slot == -1 && (type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
+ error("invalid chunk type %llu", type);
+ return -EIO;
+ }
+ if (type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
+ error("unrecognized chunk type: %llu",
+ ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+ BTRFS_BLOCK_GROUP_PROFILE_MASK) & type);
+ return -EIO;
+ }
+ /*
+ * Btrfs_chunk contains at least one stripe, and for sys_chunk
+ * it can't exceed the system chunk array size
+ * For normal chunk, it should match its chunk item size.
+ */
+ if (num_stripes < 1 ||
+ (slot == -1 && sizeof(struct btrfs_stripe) * num_stripes >
+ BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
+ (slot >= 0 && sizeof(struct btrfs_stripe) * (num_stripes - 1) >
+ btrfs_item_size_nr(leaf, slot))) {
+ error("invalid num_stripes: %u", num_stripes);
+ return -EIO;
+ }
+ /*
+ * Device number check against profile
+ */
+ if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes == 0) ||
+ (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
+ (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
+ (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
+ (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
+ ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
+ num_stripes != 1)) {
+ error("Invalid num_stripes:sub_stripes %u:%u for profile %llu",
+ num_stripes, sub_stripes,
+ type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Slot is used to verify the chunk item is valid
*
* For sys chunk in superblock, pass -1 to indicate sys chunk.
*/
logical = key->offset;
length = btrfs_chunk_length(leaf, chunk);
+ num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+ /* Validation check */
+ ret = btrfs_check_chunk_valid(root, leaf, chunk, slot, logical);
+ if (ret) {
+ error("%s checksums match, but it has an invalid chunk, %s",
+ (slot == -1) ? "Superblock" : "Metadata",
+ (slot == -1) ? "try btrfsck --repair -s <superblock> ie, 0,1,2" : "");
+ return ret;
+ }
ce = search_cache_extent(&map_tree->cache_tree, logical);
return 0;
}
- num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
map = kmalloc(btrfs_map_lookup_size(num_stripes), GFP_NOFS);
if (!map)
return -ENOMEM;
map->type = btrfs_chunk_type(leaf, chunk);
map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
- /* Check on chunk item type */
- if (map->type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
- BTRFS_BLOCK_GROUP_PROFILE_MASK)) {
- fprintf(stderr, "Unknown chunk type bits: %llu\n",
- map->type & ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
- BTRFS_BLOCK_GROUP_PROFILE_MASK));
- ret = -EIO;
- goto out;
- }
-
- /*
- * Btrfs_chunk contains at least one stripe, and for sys_chunk
- * it can't exceed the system chunk array size
- * For normal chunk, it should match its chunk item size.
- */
- if (num_stripes < 1 ||
- (slot == -1 && sizeof(struct btrfs_stripe) * num_stripes >
- BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) ||
- (slot >= 0 && sizeof(struct btrfs_stripe) * (num_stripes - 1) >
- btrfs_item_size_nr(leaf, slot))) {
- fprintf(stderr, "Invalid num_stripes: %u\n",
- num_stripes);
- ret = -EIO;
- goto out;
- }
-
- /*
- * Device number check against profile
- */
- if ((map->type & BTRFS_BLOCK_GROUP_RAID10 && map->sub_stripes == 0) ||
- (map->type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
- (map->type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
- (map->type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
- (map->type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
- ((map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
- num_stripes != 1)) {
- fprintf(stderr,
- "Invalid num_stripes:sub_stripes %u:%u for profile %llu\n",
- num_stripes, map->sub_stripes,
- map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
- ret = -EIO;
- goto out;
- }
-
for (i = 0; i < num_stripes; i++) {
map->stripes[i].physical =
btrfs_stripe_offset_nr(leaf, chunk, i);
map->stripes[i].dev = fill_missing_device(devid);
printf("warning, device %llu is missing\n",
(unsigned long long)devid);
+ list_add(&map->stripes[i].dev->dev_list,
+ &root->fs_info->fs_devices->devices);
}
}
BUG_ON(ret);
return 0;
-out:
- free(map);
- return ret;
}
static int fill_device_from_item(struct extent_buffer *leaf,
device = btrfs_find_device(root, devid, dev_uuid, fs_uuid);
if (!device) {
- printk("warning devid %llu not found already\n",
- (unsigned long long)devid);
device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device)
return -ENOMEM;
struct extent_buffer *sb;
struct btrfs_disk_key *disk_key;
struct btrfs_chunk *chunk;
- struct btrfs_key key;
+ u8 *array_ptr;
+ unsigned long sb_array_offset;
+ int ret = 0;
u32 num_stripes;
+ u32 array_size;
u32 len = 0;
- u8 *ptr;
- u8 *array_end;
- int ret = 0;
+ u32 cur_offset;
+ struct btrfs_key key;
- sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET,
+ sb = btrfs_find_create_tree_block(root->fs_info,
+ BTRFS_SUPER_INFO_OFFSET,
BTRFS_SUPER_INFO_SIZE);
if (!sb)
return -ENOMEM;
btrfs_set_buffer_uptodate(sb);
write_extent_buffer(sb, super_copy, 0, sizeof(*super_copy));
- array_end = ((u8 *)super_copy->sys_chunk_array) +
- btrfs_super_sys_array_size(super_copy);
+ array_size = btrfs_super_sys_array_size(super_copy);
- /*
- * we do this loop twice, once for the device items and
- * once for all of the chunks. This way there are device
- * structs filled in for every chunk
- */
- ptr = super_copy->sys_chunk_array;
+ array_ptr = super_copy->sys_chunk_array;
+ sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+ cur_offset = 0;
+
+ while (cur_offset < array_size) {
+ disk_key = (struct btrfs_disk_key *)array_ptr;
+ len = sizeof(*disk_key);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
- while (ptr < array_end) {
- disk_key = (struct btrfs_disk_key *)ptr;
btrfs_disk_key_to_cpu(&key, disk_key);
- len = sizeof(*disk_key);
- ptr += len;
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
if (key.type == BTRFS_CHUNK_ITEM_KEY) {
- chunk = (struct btrfs_chunk *)(ptr - (u8 *)super_copy);
+ chunk = (struct btrfs_chunk *)sb_array_offset;
+ /*
+ * At least one btrfs_chunk with one stripe must be
+ * present, exact stripe count check comes afterwards
+ */
+ len = btrfs_chunk_item_size(1);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
+ num_stripes = btrfs_chunk_num_stripes(sb, chunk);
+ if (!num_stripes) {
+ printk(
+ "ERROR: invalid number of stripes %u in sys_array at offset %u\n",
+ num_stripes, cur_offset);
+ ret = -EIO;
+ break;
+ }
+
+ len = btrfs_chunk_item_size(num_stripes);
+ if (cur_offset + len > array_size)
+ goto out_short_read;
+
ret = read_one_chunk(root, &key, sb, chunk, -1);
if (ret)
break;
- num_stripes = btrfs_chunk_num_stripes(sb, chunk);
- len = btrfs_chunk_item_size(num_stripes);
} else {
- BUG();
+ printk(
+ "ERROR: unexpected item type %u in sys_array at offset %u\n",
+ (u32)key.type, cur_offset);
+ ret = -EIO;
+ break;
}
- ptr += len;
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
}
free_extent_buffer(sb);
return ret;
+
+out_short_read:
+ printk("ERROR: sys_array too short to read %u bytes at offset %u\n",
+ len, cur_offset);
+ free_extent_buffer(sb);
+ return -EIO;
}
int btrfs_read_chunk_tree(struct btrfs_root *root)
if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
break;
- eb = malloc(sizeof(struct extent_buffer) + stripe_len);
+ eb = calloc(1, sizeof(struct extent_buffer) + stripe_len);
if (!eb)
BUG();
- memset(eb, 0, sizeof(struct extent_buffer) + stripe_len);
eb->start = raid_map[i];
eb->len = stripe_len;
ebs[multi->num_stripes - 1] = p_eb;
memcpy(p_eb->data, ebs[0]->data, stripe_len);
for (j = 1; j < multi->num_stripes - 1; j++) {
- for (i = 0; i < stripe_len; i += sizeof(unsigned long)) {
- *(unsigned long *)(p_eb->data + i) ^=
- *(unsigned long *)(ebs[j]->data + i);
+ for (i = 0; i < stripe_len; i += sizeof(u64)) {
+ u64 p_eb_data;
+ u64 ebs_data;
+
+ p_eb_data = get_unaligned_64(p_eb->data + i);
+ ebs_data = get_unaligned_64(ebs[j]->data + i);
+ p_eb_data ^= ebs_data;
+ put_unaligned_64(p_eb_data, p_eb->data + i);
}
}
}