* Boston, MA 021110-1307, USA.
*/
+#include "kerncompat.h"
+#include "androidcompat.h"
+
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <uuid/uuid.h>
#include <pthread.h>
-#include "kerncompat.h"
#include "list.h"
#include "radix-tree.h"
#include "ctree.h"
#include "transaction.h"
#include "crc32c.h"
#include "utils.h"
-#include "version.h"
#include "btrfsck.h"
#include "commands.h"
u16 csum_size;
u32 sectorsize;
- u32 leafsize;
+ u32 nodesize;
u64 generation;
u64 chunk_root_generation;
struct recover_control *rc;
struct btrfs_device *dev;
int fd;
+ u64 bytenr;
};
static struct extent_record *btrfs_new_extent_record(struct extent_buffer *eb)
{
struct extent_record *rec;
- rec = malloc(sizeof(*rec));
+ rec = calloc(1, sizeof(*rec));
if (!rec) {
fprintf(stderr, "Fail to allocate memory for extent record.\n");
exit(1);
}
- memset(rec, 0, sizeof(*rec));
rec->cache.start = btrfs_header_bytenr(eb);
rec->cache.size = eb->len;
rec->generation = btrfs_header_generation(eb);
generation);
/*
* According to the current kernel code, the following
- * case is impossble, or there is something wrong in
+ * case is impossible, or there is something wrong in
* the kernel code.
*/
if (memcmp(((void *)exist) + offset,
list_del_init(&exist->list);
free(exist);
/*
- * We must do seach again to avoid the following cache.
+ * We must do search again to avoid the following cache.
* /--old bg 1--//--old bg 2--/
* /--new bg--/
*/
printf("\n");
}
-static void print_device_info(struct btrfs_device *device, char *prefix)
-{
- if (prefix)
- printf("%s", prefix);
- printf("Device: id = %llu, name = %s\n",
- device->devid, device->name);
-}
-
-static void print_all_devices(struct list_head *devices)
-{
- struct btrfs_device *dev;
-
- printf("All Devices:\n");
- list_for_each_entry(dev, devices, dev_list)
- print_device_info(dev, "\t");
- printf("\n");
-}
-
static void print_scan_result(struct recover_control *rc)
{
if (!rc->verbose)
printf("DEVICE SCAN RESULT:\n");
printf("Filesystem Information:\n");
printf("\tsectorsize: %d\n", rc->sectorsize);
- printf("\tleafsize: %d\n", rc->leafsize);
+ printf("\tnodesize: %d\n", rc->nodesize);
printf("\ttree root generation: %llu\n", rc->generation);
printf("\tchunk root generation: %llu\n", rc->chunk_root_generation);
printf("\n");
btrfs_dev_extent_chunk_offset(l, dev_extent)) {
if (rc->verbose)
fprintf(stderr,
- "Device tree unmatch with chunks dev_extent[%llu, %llu], chunk[%llu, %llu]\n",
+ "Device tree mismatch with chunks dev_extent[%llu, %llu], chunk[%llu, %llu]\n",
btrfs_dev_extent_chunk_offset(l,
dev_extent),
btrfs_dev_extent_length(l, dev_extent),
if (chunk->type_flags != btrfs_disk_block_group_flags(l, bg_ptr)) {
if (rc->verbose)
fprintf(stderr,
- "Chunk[%llu, %llu]'s type(%llu) is differemt with Block Group's type(%llu)\n",
+ "Chunk[%llu, %llu]'s type(%llu) is different with Block Group's type(%llu)\n",
chunk->offset, chunk->length, chunk->type_flags,
btrfs_disk_block_group_flags(l, bg_ptr));
btrfs_release_path(&path);
if (ret)
return 1;
- buf = malloc(sizeof(*buf) + rc->leafsize);
+ buf = malloc(sizeof(*buf) + rc->nodesize);
if (!buf)
return -ENOMEM;
- buf->len = rc->leafsize;
+ buf->len = rc->nodesize;
bytenr = 0;
while (1) {
+ dev_scan->bytenr = bytenr;
+
if (is_super_block_address(bytenr))
bytenr += rc->sectorsize;
- if (pread64(fd, buf->data, rc->leafsize, bytenr) <
- rc->leafsize)
+ if (pread64(fd, buf->data, rc->nodesize, bytenr) <
+ rc->nodesize)
break;
if (memcmp_extent_buffer(buf, rc->fs_devices->fsid,
break;
}
next_node:
- bytenr += rc->leafsize;
+ bytenr += rc->nodesize;
}
out:
close(fd);
struct btrfs_device *dev;
struct device_scan *dev_scans;
pthread_t *t_scans;
- int *t_rets;
+ long *t_rets;
int devnr = 0;
int devidx = 0;
- int cancel_from = 0;
- int cancel_to = 0;
int i;
+ int all_done;
list_for_each_entry(dev, &rc->fs_devices->devices, dev_list)
devnr++;
if (!dev_scans)
return -ENOMEM;
t_scans = (pthread_t *)malloc(sizeof(pthread_t) * devnr);
- if (!t_scans)
+ if (!t_scans) {
+ free(dev_scans);
return -ENOMEM;
- t_rets = (int *)malloc(sizeof(int) * devnr);
- if (!t_rets)
+ }
+ t_rets = (long *)malloc(sizeof(long) * devnr);
+ if (!t_rets) {
+ free(dev_scans);
+ free(t_scans);
return -ENOMEM;
+ }
list_for_each_entry(dev, &rc->fs_devices->devices, dev_list) {
fd = open(dev->name, O_RDONLY);
dev_scans[devidx].rc = rc;
dev_scans[devidx].dev = dev;
dev_scans[devidx].fd = fd;
- ret = pthread_create(&t_scans[devidx], NULL,
- (void *)scan_one_device,
- (void *)&dev_scans[devidx]);
- if (ret) {
- cancel_from = 0;
- cancel_to = devidx - 1;
- goto out1;
- }
+ dev_scans[devidx].bytenr = -1;
devidx++;
}
- i = 0;
- while (i < devidx) {
- ret = pthread_join(t_scans[i], (void **)&t_rets[i]);
- if (ret || t_rets[i]) {
- ret = 1;
- cancel_from = i + 1;
- cancel_to = devnr - 1;
+ for (i = 0; i < devidx; i++) {
+ ret = pthread_create(&t_scans[i], NULL,
+ (void *)scan_one_device,
+ (void *)&dev_scans[i]);
+ if (ret)
goto out1;
+
+ dev_scans[i].bytenr = 0;
+ }
+
+ while (1) {
+ all_done = 1;
+ for (i = 0; i < devidx; i++) {
+ if (dev_scans[i].bytenr == -1)
+ continue;
+ ret = pthread_tryjoin_np(t_scans[i],
+ (void **)&t_rets[i]);
+ if (ret == EBUSY) {
+ all_done = 0;
+ continue;
+ }
+ if (ret || t_rets[i]) {
+ ret = 1;
+ goto out1;
+ }
+ dev_scans[i].bytenr = -1;
+ }
+
+ printf("\rScanning: ");
+ for (i = 0; i < devidx; i++) {
+ if (dev_scans[i].bytenr == -1)
+ printf("%sDONE in dev%d",
+ i ? ", " : "", i);
+ else
+ printf("%s%llu in dev%d",
+ i ? ", " : "", dev_scans[i].bytenr, i);
+ }
+ /* clear chars if exist in tail */
+ printf(" ");
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fflush(stdout);
+
+ if (all_done) {
+ printf("\n");
+ break;
}
- i++;
+
+ sleep(1);
}
out1:
- while (ret && (cancel_from <= cancel_to)) {
- pthread_cancel(t_scans[cancel_from]);
- cancel_from++;
+ for (i = 0; i < devidx; i++) {
+ if (dev_scans[i].bytenr == -1)
+ continue;
+ pthread_cancel(t_scans[i]);
}
out2:
free(dev_scans);
u64 devid;
u8 uuid[BTRFS_UUID_SIZE];
u16 num_stripes;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_mapping_tree *map_tree;
struct map_lookup *map;
struct stripe *stripe;
- map_tree = &root->fs_info->mapping_tree;
+ map_tree = &fs_info->mapping_tree;
num_stripes = chunk->num_stripes;
map = malloc(btrfs_map_lookup_size(num_stripes));
if (!map)
devid = stripe->devid;
memcpy(uuid, stripe->dev_uuid, BTRFS_UUID_SIZE);
map->stripes[i].physical = stripe->offset;
- map->stripes[i].dev = btrfs_find_device(root, devid,
+ map->stripes[i].dev = btrfs_find_device(fs_info, devid,
uuid, NULL);
if (!map->stripes[i].dev) {
- kfree(map);
+ free(map);
return -EIO;
}
}
key.type == BTRFS_METADATA_ITEM_KEY) {
old_val = btrfs_super_bytes_used(fs_info->super_copy);
if (key.type == BTRFS_METADATA_ITEM_KEY)
- old_val += root->leafsize;
+ old_val += fs_info->nodesize;
else
old_val += key.offset;
btrfs_set_super_bytes_used(fs_info->super_copy,
if (key.objectid < end) {
if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
- key.objectid += root->sectorsize;
+ key.objectid += fs_info->sectorsize;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
}
return ret;
}
-static int block_group_free_all_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
+static int block_group_free_all_extent(struct btrfs_root *root,
struct block_group_record *bg)
{
struct btrfs_block_group_cache *cache;
end = start + cache->key.offset - 1;
set_extent_bits(&info->block_group_cache, start, end,
- BLOCK_GROUP_DIRTY, GFP_NOFS);
- set_extent_dirty(&info->free_space_cache, start, end, GFP_NOFS);
+ BLOCK_GROUP_DIRTY);
+ set_extent_dirty(&info->free_space_cache, start, end);
btrfs_set_block_group_used(&cache->item, 0);
if (ret)
return ret;
- ret = block_group_free_all_extent(trans, root, chunk->bg_rec);
+ ret = block_group_free_all_extent(root, chunk->bg_rec);
if (ret)
return ret;
}
if (min_devid > dev->devid)
min_devid = dev->devid;
}
- disk_key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
- disk_key.type = BTRFS_DEV_ITEM_KEY;
- disk_key.offset = min_devid;
+ btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, min_devid);
- cow = btrfs_alloc_free_block(trans, root, root->nodesize,
+ cow = btrfs_alloc_free_block(trans, root, root->fs_info->nodesize,
BTRFS_CHUNK_TREE_OBJECTID,
&disk_key, 0, 0, 0);
btrfs_set_header_bytenr(cow, cow->start);
{
struct btrfs_device *dev;
struct btrfs_key key;
- struct btrfs_dev_item *dev_item;
+ struct btrfs_dev_item dev_item_tmp;
+ struct btrfs_dev_item *dev_item = &dev_item_tmp;
int ret = 0;
- dev_item = malloc(sizeof(struct btrfs_dev_item));
- if (!dev_item)
- return -ENOMEM;
-
list_for_each_entry(dev, &rc->fs_devices->devices, dev_list) {
key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
key.type = BTRFS_DEV_ITEM_KEY;
dev_item, sizeof(*dev_item));
}
- free(dev_item);
return ret;
}
key.offset = chunk_rec->offset;
ret = btrfs_insert_item(trans, chunk_root, &key, chunk,
- btrfs_chunk_item_size(chunk->num_stripes));
+ btrfs_chunk_item_size(chunk_rec->num_stripes));
free(chunk);
return ret;
}
static int rebuild_sys_array(struct recover_control *rc,
struct btrfs_root *root)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_chunk *chunk;
struct btrfs_key key;
struct chunk_record *chunk_rec;
int ret = 0;
u16 num_stripes;
- btrfs_set_super_sys_array_size(root->fs_info->super_copy, 0);
+ btrfs_set_super_sys_array_size(fs_info->super_copy, 0);
list_for_each_entry(chunk_rec, &rc->good_chunks, list) {
if (!(chunk_rec->type_flags & BTRFS_BLOCK_GROUP_SYSTEM))
key.type = BTRFS_CHUNK_ITEM_KEY;
key.offset = chunk_rec->offset;
- ret = btrfs_add_system_chunk(NULL, root, &key, chunk,
+ ret = btrfs_add_system_chunk(fs_info, &key, chunk,
btrfs_chunk_item_size(num_stripes));
free(chunk);
if (ret)
found_key.type != BTRFS_EXTENT_DATA_KEY)
goto next;
if (found_key.type == BTRFS_METADATA_ITEM_KEY)
- used_ret += extent_root->nodesize;
+ used_ret += extent_root->fs_info->nodesize;
else
used_ret += found_key.offset;
next:
{
struct chunk_record *chunk_rec;
struct btrfs_key search_key;
- struct btrfs_path *path;
+ struct btrfs_path path;
u64 used = 0;
int ret = 0;
if (list_empty(&rc->rebuild_chunks))
return 0;
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
list_for_each_entry(chunk_rec, &rc->rebuild_chunks, list) {
search_key.objectid = chunk_rec->offset;
search_key.type = BTRFS_EXTENT_ITEM_KEY;
search_key.offset = 0;
ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
- &search_key, path, 0, 0);
+ &search_key, &path, 0, 0);
if (ret < 0)
goto out;
ret = calculate_bg_used(root->fs_info->extent_root,
- chunk_rec, path, &used);
+ chunk_rec, &path, &used);
/*
* Extent tree is damaged, better to rebuild the whole extent
* tree. Currently, change the used to chunk's len to prevent
"Mark the block group full to prevent block rsv problems\n");
used = chunk_rec->length;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
ret = __insert_block_group(trans, chunk_rec,
root->fs_info->extent_root,
used);
goto out;
}
out:
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
struct btrfs_fs_info *fs_info;
struct btrfs_super_block *disk_super;
struct extent_buffer *eb;
- u32 sectorsize;
- u32 nodesize;
- u32 leafsize;
- u32 stripesize;
int ret;
fs_info = btrfs_new_fs_info(1, BTRFS_SUPER_INFO_OFFSET);
disk_super = fs_info->super_copy;
ret = btrfs_read_dev_super(fs_info->fs_devices->latest_bdev,
- disk_super, fs_info->super_bytenr, 1);
+ disk_super, fs_info->super_bytenr,
+ SBREAD_RECOVER);
if (ret) {
fprintf(stderr, "No valid btrfs found\n");
goto out_devices;
}
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
+ fs_info->sectorsize = btrfs_super_sectorsize(disk_super);
+ fs_info->nodesize = btrfs_super_nodesize(disk_super);
+ fs_info->stripesize = btrfs_super_stripesize(disk_super);
- ret = btrfs_check_fs_compatibility(disk_super, 1);
+ ret = btrfs_check_fs_compatibility(disk_super, OPEN_CTREE_WRITES);
if (ret)
goto out_devices;
- nodesize = btrfs_super_nodesize(disk_super);
- leafsize = btrfs_super_leafsize(disk_super);
- sectorsize = btrfs_super_sectorsize(disk_super);
- stripesize = btrfs_super_stripesize(disk_super);
-
- __setup_root(nodesize, leafsize, sectorsize, stripesize,
- fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
+ btrfs_setup_root(fs_info->chunk_root, fs_info,
+ BTRFS_CHUNK_TREE_OBJECTID);
ret = build_device_maps_by_chunk_records(rc, fs_info->chunk_root);
if (ret)
int ret;
int fd;
struct btrfs_super_block *sb;
+ char buf[BTRFS_SUPER_INFO_SIZE];
struct btrfs_fs_devices *fs_devices;
ret = 0;
return -1;
}
- sb = malloc(BTRFS_SUPER_INFO_SIZE);
- if (!sb) {
- fprintf(stderr, "allocating memory for sb failed.\n");
- ret = -ENOMEM;
- goto fail_close_fd;
- }
-
- ret = btrfs_read_dev_super(fd, sb, BTRFS_SUPER_INFO_OFFSET, 1);
+ sb = (struct btrfs_super_block*)buf;
+ ret = btrfs_read_dev_super(fd, sb, BTRFS_SUPER_INFO_OFFSET,
+ SBREAD_RECOVER);
if (ret) {
fprintf(stderr, "read super block error\n");
- goto fail_free_sb;
+ goto out_close_fd;
}
rc->sectorsize = btrfs_super_sectorsize(sb);
- rc->leafsize = btrfs_super_leafsize(sb);
+ rc->nodesize = btrfs_super_nodesize(sb);
rc->generation = btrfs_super_generation(sb);
rc->chunk_root_generation = btrfs_super_chunk_root_generation(sb);
rc->csum_size = btrfs_super_csum_size(sb);
if (btrfs_super_flags(sb) & BTRFS_SUPER_FLAG_SEEDING) {
fprintf(stderr, "this device is seed device\n");
ret = -1;
- goto fail_free_sb;
+ goto out_close_fd;
}
- ret = btrfs_scan_fs_devices(fd, path, &fs_devices, 0, 1);
+ ret = btrfs_scan_fs_devices(fd, path, &fs_devices, 0, SBREAD_RECOVER, 0);
if (ret)
- goto fail_free_sb;
+ goto out_close_fd;
rc->fs_devices = fs_devices;
if (rc->verbose)
print_all_devices(&rc->fs_devices->devices);
-fail_free_sb:
- free(sb);
-fail_close_fd:
+out_close_fd:
close(fd);
return ret;
}
struct list_head *devexts, int ndevexts)
{
struct device_extent_record *devext;
- u64 strpie_length;
+ u64 stripe_length;
int expected_num_stripes;
expected_num_stripes = calc_num_stripes(bg->flags);
if (expected_num_stripes && expected_num_stripes != ndevexts)
return 1;
- strpie_length = calc_stripe_length(bg->flags, bg->offset, ndevexts);
+ if (check_num_stripes(bg->flags, ndevexts) < 0)
+ return 1;
+
+ stripe_length = calc_stripe_length(bg->flags, bg->offset, ndevexts);
list_for_each_entry(devext, devexts, chunk_list) {
- if (devext->length != strpie_length)
+ if (devext->length != stripe_length)
return 1;
}
return 0;
int ret = 0;
struct btrfs_root *csum_root = root->fs_info->csum_root;
struct btrfs_csum_item *csum_item;
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
int csums_in_item = btrfs_item_size_nr(*leaf, *slot) / csum_size;
goto out;
}
ret = 0;
- csum_result = btrfs_csum_data(NULL, data, csum_result, len);
- btrfs_csum_final(csum_result, (char *)&csum_result);
+ csum_result = btrfs_csum_data(data, csum_result, len);
+ btrfs_csum_final(csum_result, (u8 *)&csum_result);
if (csum_result != tree_csum)
ret = 1;
out:
static u64 item_end_offset(struct btrfs_root *root, struct btrfs_key *key,
struct extent_buffer *leaf, int slot) {
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
u64 offset = btrfs_item_size_nr(leaf, slot);
dev = btrfs_find_device_by_devid(rc->fs_devices, devext->objectid,
0);
if (!dev)
- return 1;
- BUG_ON(btrfs_find_device_by_devid(rc->fs_devices, devext->objectid,
- 1));
+ return -ENOENT;
+ if (btrfs_find_device_by_devid(rc->fs_devices, devext->objectid, 1)) {
+ error("unexpected: found another device with id %llu",
+ (unsigned long long)devext->objectid);
+ return -EINVAL;
+ }
chunk->stripes[index].devid = devext->objectid;
chunk->stripes[index].offset = devext->offset;
u64 chunk_end = chunk->offset + chunk->length;
u64 csum_offset = 0;
u64 data_offset;
- u32 blocksize = root->sectorsize;
+ u32 blocksize = root->fs_info->sectorsize;
u32 tree_csum;
int index = 0;
int num_unordered = 0;
nstripes = btrfs_get_device_extents(bg->objectid,
&rc->devext.no_chunk_orphans,
&devexts);
- chunk = malloc(btrfs_chunk_record_size(nstripes));
+ chunk = calloc(1, btrfs_chunk_record_size(nstripes));
if (!chunk)
return -ENOMEM;
- memset(chunk, 0, btrfs_chunk_record_size(nstripes));
INIT_LIST_HEAD(&chunk->dextents);
chunk->bg_rec = bg;
chunk->cache.start = bg->objectid;
chunk->sub_stripes = calc_sub_nstripes(bg->flags);
ret = insert_cache_extent(&rc->chunk, &chunk->cache);
+ if (ret == -EEXIST) {
+ error("duplicate entry in cache start %llu size %llu",
+ (unsigned long long)chunk->cache.start,
+ (unsigned long long)chunk->cache.size);
+ free(chunk);
+ return ret;
+ }
BUG_ON(ret);
list_del_init(&bg->list);
}
/*
- * Return 0 when succesful, < 0 on error and > 0 if aborted by user
+ * Return 0 when successful, < 0 on error and > 0 if aborted by user
*/
int btrfs_recover_chunk_tree(char *path, int verbose, int yes)
{
}
trans = btrfs_start_transaction(root, 1);
+ BUG_ON(IS_ERR(trans));
ret = remove_chunk_extent_item(trans, &rc, root);
BUG_ON(ret);