#include "transaction.h"
#include "crc32c.h"
#include "utils.h"
+#include "print-tree.h"
+
+static int close_all_devices(struct btrfs_fs_info *fs_info);
static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
{
- if (buf->start != btrfs_header_bytenr(buf))
- BUG();
- if (memcmp_extent_buffer(buf, root->fs_info->fsid,
- (unsigned long)btrfs_header_fsid(buf),
- BTRFS_FSID_SIZE))
- BUG();
- return 0;
+ struct btrfs_fs_devices *fs_devices;
+ int ret = 1;
+
+ if (buf->start != btrfs_header_bytenr(buf)) {
+ printk("Check tree block failed, want=%Lu, have=%Lu\n",
+ buf->start, btrfs_header_bytenr(buf));
+ return ret;
+ }
+
+ fs_devices = root->fs_info->fs_devices;
+ while (fs_devices) {
+ if (!memcmp_extent_buffer(buf, fs_devices->fsid,
+ (unsigned long)btrfs_header_fsid(buf),
+ BTRFS_FSID_SIZE)) {
+ ret = 0;
+ break;
+ }
+ fs_devices = fs_devices->seed;
+ }
+ return ret;
}
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
*(__le32 *)result = ~cpu_to_le32(crc);
}
-static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
- int verify)
+int csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
+ int verify)
{
- char result[BTRFS_CRC32_SIZE];
+ char *result;
u32 len;
u32 crc = ~(u32)0;
+ result = malloc(csum_size * sizeof(char));
+ if (!result)
+ return 1;
+
len = buf->len - BTRFS_CSUM_SIZE;
crc = crc32c(crc, buf->data + BTRFS_CSUM_SIZE, len);
btrfs_csum_final(crc, result);
if (verify) {
- if (memcmp_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE)) {
- printk("checksum verify failed on %llu\n",
- (unsigned long long)buf->start);
+ if (memcmp_extent_buffer(buf, result, 0, csum_size)) {
+ printk("checksum verify failed on %llu found %X "
+ "wanted %X\n", (unsigned long long)buf->start,
+ *((int *)result), *((char *)buf->data));
+ free(result);
return 1;
}
} else {
- write_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE);
+ write_extent_buffer(buf, result, 0, csum_size);
}
+ free(result);
return 0;
}
+int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
+ int verify)
+{
+ u16 csum_size =
+ btrfs_super_csum_size(&root->fs_info->super_copy);
+ return csum_tree_block_size(buf, csum_size, verify);
+}
+
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
u64 bytenr, u32 blocksize)
{
blocksize);
}
-int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize)
+int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
+ u64 parent_transid)
{
int ret;
- int total_devs = 1;
- int dev_nr;
struct extent_buffer *eb;
- u64 physical;
u64 length;
+ struct btrfs_multi_bio *multi = NULL;
struct btrfs_device *device;
eb = btrfs_find_tree_block(root, bytenr, blocksize);
- if (eb && btrfs_buffer_uptodate(eb)) {
+ if (eb && btrfs_buffer_uptodate(eb, parent_transid)) {
free_extent_buffer(eb);
return 0;
}
- dev_nr = 0;
- ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, dev_nr,
- bytenr, &physical, &length, &device,
- &total_devs);
+ length = blocksize;
+ ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
+ bytenr, &length, &multi, 0, NULL);
BUG_ON(ret);
+ device = multi->stripes[0].dev;
device->total_ios++;
blocksize = min(blocksize, (u32)(64 * 1024));
- readahead(device->fd, physical, blocksize);
+ readahead(device->fd, multi->stripes[0].physical, blocksize);
+ kfree(multi);
+ return 0;
+}
+
+static int verify_parent_transid(struct extent_io_tree *io_tree,
+ struct extent_buffer *eb, u64 parent_transid,
+ int ignore)
+{
+ int ret;
+
+ if (!parent_transid || btrfs_header_generation(eb) == parent_transid)
+ return 0;
+
+ if (extent_buffer_uptodate(eb) &&
+ btrfs_header_generation(eb) == parent_transid) {
+ ret = 0;
+ goto out;
+ }
+ printk("parent transid verify failed on %llu wanted %llu found %llu\n",
+ (unsigned long long)eb->start,
+ (unsigned long long)parent_transid,
+ (unsigned long long)btrfs_header_generation(eb));
+ if (ignore) {
+ printk("Ignoring transid failure\n");
+ return 0;
+ }
+
+ ret = 1;
+out:
+ clear_extent_buffer_uptodate(io_tree, eb);
+ return ret;
+
+}
+
+
+static int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror)
+{
+ unsigned long offset = 0;
+ struct btrfs_multi_bio *multi = NULL;
+ struct btrfs_device *device;
+ int ret = 0;
+ u64 read_len;
+ unsigned long bytes_left = eb->len;
+
+ while (bytes_left) {
+ read_len = bytes_left;
+ ret = btrfs_map_block(&info->mapping_tree, READ,
+ eb->start + offset, &read_len, &multi,
+ mirror, NULL);
+ if (ret) {
+ printk("Couldn't map the block %Lu\n", eb->start + offset);
+ return -EIO;
+ }
+ device = multi->stripes[0].dev;
+
+ if (device->fd == 0)
+ return -EIO;
+
+ eb->fd = device->fd;
+ device->total_ios++;
+ eb->dev_bytenr = multi->stripes[0].physical;
+ kfree(multi);
+
+ if (read_len > bytes_left)
+ read_len = bytes_left;
+
+ ret = read_extent_from_disk(eb, offset, read_len);
+ if (ret)
+ return -EIO;
+ offset += read_len;
+ bytes_left -= read_len;
+ }
return 0;
}
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
- u32 blocksize)
+ u32 blocksize, u64 parent_transid)
{
int ret;
- int total_devs = 1;
- int dev_nr;
struct extent_buffer *eb;
- u64 physical;
- u64 length;
- struct btrfs_device *device;
+ u64 best_transid = 0;
+ int mirror_num = 0;
+ int good_mirror = 0;
+ int num_copies;
+ int ignore = 0;
eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
if (!eb)
return NULL;
- if (btrfs_buffer_uptodate(eb))
+ if (btrfs_buffer_uptodate(eb, parent_transid))
return eb;
- dev_nr = 0;
- ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, dev_nr,
- eb->start, &physical, &length, &device,
- &total_devs);
- BUG_ON(ret);
- eb->fd = device->fd;
- device->total_ios++;
- eb->dev_bytenr = physical;
- ret = read_extent_from_disk(eb);
- if (ret) {
- free_extent_buffer(eb);
- return NULL;
+ while (1) {
+ ret = read_whole_eb(root->fs_info, eb, mirror_num);
+ if (ret == 0 && check_tree_block(root, eb) == 0 &&
+ csum_tree_block(root, eb, 1) == 0 &&
+ verify_parent_transid(eb->tree, eb, parent_transid, ignore)
+ == 0) {
+ btrfs_set_buffer_uptodate(eb);
+ return eb;
+ }
+ if (ignore) {
+ if (check_tree_block(root, eb))
+ printk("read block failed check_tree_block\n");
+ else
+ printk("Csum didn't match\n");
+ break;
+ }
+ num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
+ eb->start, eb->len);
+ if (num_copies == 1) {
+ ignore = 1;
+ continue;
+ }
+ if (btrfs_header_generation(eb) > best_transid) {
+ best_transid = btrfs_header_generation(eb);
+ good_mirror = mirror_num;
+ }
+ mirror_num++;
+ if (mirror_num > num_copies) {
+ mirror_num = good_mirror;
+ ignore = 1;
+ continue;
+ }
+ }
+ free_extent_buffer(eb);
+ return NULL;
+}
+
+static int rmw_eb(struct btrfs_fs_info *info,
+ struct extent_buffer *eb, struct extent_buffer *orig_eb)
+{
+ int ret;
+ unsigned long orig_off = 0;
+ unsigned long dest_off = 0;
+ unsigned long copy_len = eb->len;
+
+ ret = read_whole_eb(info, eb, 0);
+ if (ret)
+ return ret;
+
+ if (eb->start + eb->len <= orig_eb->start ||
+ eb->start >= orig_eb->start + orig_eb->len)
+ return 0;
+ /*
+ * | ----- orig_eb ------- |
+ * | ----- stripe ------- |
+ * | ----- orig_eb ------- |
+ * | ----- orig_eb ------- |
+ */
+ if (eb->start > orig_eb->start)
+ orig_off = eb->start - orig_eb->start;
+ if (orig_eb->start > eb->start)
+ dest_off = orig_eb->start - eb->start;
+
+ if (copy_len > orig_eb->len - orig_off)
+ copy_len = orig_eb->len - orig_off;
+ if (copy_len > eb->len - dest_off)
+ copy_len = eb->len - dest_off;
+
+ memcpy(eb->data + dest_off, orig_eb->data + orig_off, copy_len);
+ return 0;
+}
+
+static void split_eb_for_raid56(struct btrfs_fs_info *info,
+ struct extent_buffer *orig_eb,
+ struct extent_buffer **ebs,
+ u64 stripe_len, u64 *raid_map,
+ int num_stripes)
+{
+ struct extent_buffer *eb;
+ u64 start = orig_eb->start;
+ u64 this_eb_start;
+ int i;
+ int ret;
+
+ for (i = 0; i < num_stripes; i++) {
+ if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
+ break;
+
+ eb = malloc(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;
+ eb->refs = 1;
+ eb->flags = 0;
+ eb->fd = -1;
+ eb->dev_bytenr = (u64)-1;
+
+ this_eb_start = raid_map[i];
+
+ if (start > this_eb_start ||
+ start + orig_eb->len < this_eb_start + stripe_len) {
+ ret = rmw_eb(info, eb, orig_eb);
+ BUG_ON(ret);
+ } else {
+ memcpy(eb->data, orig_eb->data + eb->start - start, stripe_len);
+ }
+ ebs[i] = eb;
+ }
+}
+
+static int write_raid56_with_parity(struct btrfs_fs_info *info,
+ struct extent_buffer *eb,
+ struct btrfs_multi_bio *multi,
+ u64 stripe_len, u64 *raid_map)
+{
+ struct extent_buffer *ebs[multi->num_stripes], *p_eb = NULL, *q_eb = NULL;
+ int i;
+ int j;
+ int ret;
+ int alloc_size = eb->len;
+
+ if (stripe_len > alloc_size)
+ alloc_size = stripe_len;
+
+ split_eb_for_raid56(info, eb, ebs, stripe_len, raid_map,
+ multi->num_stripes);
+
+ for (i = 0; i < multi->num_stripes; i++) {
+ struct extent_buffer *new_eb;
+ if (raid_map[i] < BTRFS_RAID5_P_STRIPE) {
+ ebs[i]->dev_bytenr = multi->stripes[i].physical;
+ ebs[i]->fd = multi->stripes[i].dev->fd;
+ multi->stripes[i].dev->total_ios++;
+ BUG_ON(ebs[i]->start != raid_map[i]);
+ continue;
+ }
+ new_eb = kmalloc(sizeof(*eb) + alloc_size, GFP_NOFS);
+ BUG_ON(!new_eb);
+ new_eb->dev_bytenr = multi->stripes[i].physical;
+ new_eb->fd = multi->stripes[i].dev->fd;
+ multi->stripes[i].dev->total_ios++;
+ new_eb->len = stripe_len;
+
+ if (raid_map[i] == BTRFS_RAID5_P_STRIPE)
+ p_eb = new_eb;
+ else if (raid_map[i] == BTRFS_RAID6_Q_STRIPE)
+ q_eb = new_eb;
+ }
+ if (q_eb) {
+ void *pointers[multi->num_stripes];
+ ebs[multi->num_stripes - 2] = p_eb;
+ ebs[multi->num_stripes - 1] = q_eb;
+
+ for (i = 0; i < multi->num_stripes; i++)
+ pointers[i] = ebs[i]->data;
+
+ raid6_gen_syndrome(multi->num_stripes, stripe_len, pointers);
+ } else {
+ 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 < multi->num_stripes; i++) {
+ ret = write_extent_to_disk(ebs[i]);
+ BUG_ON(ret);
+ if (ebs[i] != eb)
+ kfree(ebs[i]);
}
- btrfs_set_buffer_uptodate(eb);
- return eb;
+ return 0;
}
int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *eb)
{
int ret;
- int total_devs = 1;
int dev_nr;
- u64 physical;
u64 length;
- struct btrfs_device *device;
+ u64 *raid_map = NULL;
+ struct btrfs_multi_bio *multi = NULL;
if (check_tree_block(root, eb))
BUG();
- if (!btrfs_buffer_uptodate(eb))
+ if (!btrfs_buffer_uptodate(eb, trans->transid))
BUG();
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
csum_tree_block(root, eb, 0);
dev_nr = 0;
- while(dev_nr < total_devs) {
- ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE,
- dev_nr, eb->start, &physical, &length,
- &device, &total_devs);
+ length = eb->len;
+ ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE,
+ eb->start, &length, &multi, 0, &raid_map);
+
+ if (raid_map) {
+ ret = write_raid56_with_parity(root->fs_info, eb, multi,
+ length, raid_map);
BUG_ON(ret);
- eb->fd = device->fd;
- eb->dev_bytenr = physical;
+ } else while (dev_nr < multi->num_stripes) {
+ BUG_ON(ret);
+ eb->fd = multi->stripes[dev_nr].dev->fd;
+ eb->dev_bytenr = multi->stripes[dev_nr].physical;
+ multi->stripes[dev_nr].dev->total_ios++;
dev_nr++;
- device->total_ios++;
ret = write_extent_to_disk(eb);
BUG_ON(ret);
}
+ kfree(multi);
return 0;
}
-static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
+int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
u32 stripesize, struct btrfs_root *root,
struct btrfs_fs_info *fs_info, u64 objectid)
{
break;
btrfs_set_root_bytenr(&root->root_item,
root->node->start);
+ btrfs_set_root_generation(&root->root_item,
+ trans->transid);
root->root_item.level = btrfs_header_level(root->node);
ret = btrfs_update_root(trans, tree_root,
&root->root_key,
{
struct btrfs_root *root;
struct list_head *next;
+ struct extent_buffer *eb;
+ int ret;
+
+ if (fs_info->readonly)
+ return 0;
+
+ eb = fs_info->tree_root->node;
+ extent_buffer_get(eb);
+ ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb);
+ free_extent_buffer(eb);
+ if (ret)
+ return ret;
while(!list_empty(&fs_info->dirty_cowonly_roots)) {
next = fs_info->dirty_cowonly_roots.next;
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
+ u64 transid = trans->transid;
int ret = 0;
- struct btrfs_root *new_root = NULL;
struct btrfs_fs_info *fs_info = root->fs_info;
if (root->commit_root == root->node)
goto commit_tree;
- new_root = malloc(sizeof(*new_root));
- if (!new_root)
- return -ENOMEM;
- memcpy(new_root, root, sizeof(*new_root));
- new_root->node = root->commit_root;
+ free_extent_buffer(root->commit_root);
root->commit_root = NULL;
- root->root_key.offset = trans->transid;
btrfs_set_root_bytenr(&root->root_item, root->node->start);
+ btrfs_set_root_generation(&root->root_item, trans->transid);
root->root_item.level = btrfs_header_level(root->node);
- ret = btrfs_insert_root(trans, fs_info->tree_root,
- &root->root_key, &root->root_item);
- BUG_ON(ret);
-
- btrfs_set_root_refs(&new_root->root_item, 0);
ret = btrfs_update_root(trans, root->fs_info->tree_root,
- &new_root->root_key, &new_root->root_item);
- BUG_ON(ret);
-
- ret = commit_tree_roots(trans, fs_info);
- BUG_ON(ret);
- ret = __commit_transaction(trans, root);
- BUG_ON(ret);
- write_ctree_super(trans, root);
- btrfs_finish_extent_commit(trans, fs_info->extent_root,
- &fs_info->pinned_extents);
- btrfs_free_transaction(root, trans);
- fs_info->running_transaction = NULL;
-
- trans = btrfs_start_transaction(root, 1);
- ret = btrfs_drop_snapshot(trans, new_root);
- BUG_ON(ret);
- ret = btrfs_del_root(trans, fs_info->tree_root, &new_root->root_key);
+ &root->root_key, &root->root_item);
BUG_ON(ret);
commit_tree:
ret = commit_tree_roots(trans, fs_info);
free_extent_buffer(root->commit_root);
root->commit_root = NULL;
fs_info->running_transaction = NULL;
- if (new_root) {
- free_extent_buffer(new_root->node);
- free(new_root);
- }
+ fs_info->last_trans_committed = transid;
return 0;
}
{
int ret;
u32 blocksize;
+ u64 generation;
__setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
root, fs_info, objectid);
ret = btrfs_find_last_root(tree_root, objectid,
&root->root_item, &root->root_key);
- BUG_ON(ret);
+ if (ret)
+ return ret;
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
+ generation = btrfs_root_generation(&root->root_item);
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
- blocksize);
- BUG_ON(!root->node);
+ blocksize, generation);
+ if (!extent_buffer_uptodate(root->node))
+ return -EIO;
+
return 0;
}
-int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
+static int find_and_setup_log_root(struct btrfs_root *tree_root,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_super_block *disk_super)
+{
+ u32 blocksize;
+ u64 blocknr = btrfs_super_log_root(disk_super);
+ struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root));
+
+ if (!log_root)
+ return -ENOMEM;
+
+ if (blocknr == 0) {
+ free(log_root);
+ return 0;
+ }
+
+ blocksize = btrfs_level_size(tree_root,
+ btrfs_super_log_root_level(disk_super));
+
+ __setup_root(tree_root->nodesize, tree_root->leafsize,
+ tree_root->sectorsize, tree_root->stripesize,
+ log_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
+
+ log_root->node = read_tree_block(tree_root, blocknr,
+ blocksize,
+ btrfs_super_generation(disk_super) + 1);
+
+ fs_info->log_root_tree = log_root;
+
+ if (!extent_buffer_uptodate(log_root->node)) {
+ free(log_root);
+ return -EIO;
+ }
+
+ free(log_root);
+ return 0;
+}
+
+
+int btrfs_free_fs_root(struct btrfs_fs_info *fs_info,
+ struct btrfs_root *root)
{
if (root->node)
free_extent_buffer(root->node);
if (root->commit_root)
free_extent_buffer(root->commit_root);
+ kfree(root);
+ return 0;
+}
+
+static int free_fs_roots(struct btrfs_fs_info *fs_info)
+{
+ struct cache_extent *cache;
+ struct btrfs_root *root;
- free(root);
+ while (1) {
+ cache = find_first_cache_extent(&fs_info->fs_root_cache, 0);
+ if (!cache)
+ break;
+ root = container_of(cache, struct btrfs_root, cache);
+ remove_cache_extent(&fs_info->fs_root_cache, cache);
+ btrfs_free_fs_root(fs_info, root);
+ }
return 0;
}
-struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
- struct btrfs_key *location)
+struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location)
{
struct btrfs_root *root;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_path *path;
struct extent_buffer *l;
+ u64 generation;
u32 blocksize;
int ret = 0;
free(root);
return ERR_PTR(ret);
}
+ generation = btrfs_root_generation(&root->root_item);
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
- blocksize);
+ blocksize, generation);
BUG_ON(!root->node);
insert:
root->ref_cows = 1;
return root;
}
-struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr)
+struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
+ struct btrfs_key *location)
{
- int fp;
+ struct btrfs_root *root;
+ struct cache_extent *cache;
+ int ret;
- fp = open(filename, O_CREAT | O_RDWR, 0600);
- if (fp < 0) {
- return NULL;
- }
- return open_ctree_fd(fp, filename, sb_bytenr);
+ if (location->objectid == BTRFS_ROOT_TREE_OBJECTID)
+ return fs_info->tree_root;
+ if (location->objectid == BTRFS_EXTENT_TREE_OBJECTID)
+ return fs_info->extent_root;
+ if (location->objectid == BTRFS_CHUNK_TREE_OBJECTID)
+ return fs_info->chunk_root;
+ if (location->objectid == BTRFS_DEV_TREE_OBJECTID)
+ return fs_info->dev_root;
+ if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
+ return fs_info->csum_root;
+
+ BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
+ location->offset != (u64)-1);
+
+ cache = find_cache_extent(&fs_info->fs_root_cache,
+ location->objectid, 1);
+ if (cache)
+ return container_of(cache, struct btrfs_root, cache);
+
+ root = btrfs_read_fs_root_no_cache(fs_info, location);
+ if (IS_ERR(root))
+ return root;
+
+ root->cache.start = location->objectid;
+ root->cache.size = 1;
+ ret = insert_existing_cache_extent(&fs_info->fs_root_cache,
+ &root->cache);
+ BUG_ON(ret);
+ return root;
}
-struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr)
+static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
+ u64 sb_bytenr,
+ u64 root_tree_bytenr, int writes,
+ int partial)
{
u32 sectorsize;
u32 nodesize;
u32 leafsize;
u32 blocksize;
u32 stripesize;
- struct btrfs_root *root = malloc(sizeof(struct btrfs_root));
+ u64 generation;
+ struct btrfs_key key;
struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
+ struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
int ret;
struct btrfs_super_block *disk_super;
struct btrfs_fs_devices *fs_devices = NULL;
u64 total_devs;
+ u64 features;
if (sb_bytenr == 0)
sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
+ /* try to drop all the caches */
+ posix_fadvise(fp, 0, 0, POSIX_FADV_DONTNEED);
+
ret = btrfs_scan_one_device(fp, path, &fs_devices,
&total_devs, sb_bytenr);
if (ret) {
fprintf(stderr, "No valid Btrfs found on %s\n", path);
- return NULL;
+ goto out;
}
- fprintf(stderr, "found Btrfs on %s with %lu devices\n", path,
- (unsigned long)total_devs);
if (total_devs != 1) {
ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1);
- BUG_ON(ret);
+ if (ret)
+ goto out;
}
- fs_info->fp = fs_devices->lowest_bdev;
- fs_info->running_transaction = NULL;
- fs_info->fs_root = root;
+ memset(fs_info, 0, sizeof(*fs_info));
fs_info->tree_root = tree_root;
fs_info->extent_root = extent_root;
- fs_info->extent_ops = NULL;
- fs_info->priv_data = NULL;
fs_info->chunk_root = chunk_root;
fs_info->dev_root = dev_root;
- fs_info->force_system_allocs = 0;
+ fs_info->csum_root = csum_root;
+
+ if (!writes)
+ fs_info->readonly = 1;
extent_io_tree_init(&fs_info->extent_cache);
extent_io_tree_init(&fs_info->free_space_cache);
extent_io_tree_init(&fs_info->pinned_extents);
extent_io_tree_init(&fs_info->pending_del);
extent_io_tree_init(&fs_info->extent_ins);
+ cache_tree_init(&fs_info->fs_root_cache);
cache_tree_init(&fs_info->mapping_tree.cache_tree);
__setup_root(4096, 4096, 4096, 4096, tree_root,
fs_info, BTRFS_ROOT_TREE_OBJECTID);
- ret = btrfs_open_devices(fs_devices, O_RDWR);
- BUG_ON(ret);
-
- fs_info->sb_buffer = btrfs_find_create_tree_block(tree_root, sb_bytenr,
- 4096);
- BUG_ON(!fs_info->sb_buffer);
- fs_info->sb_buffer->fd = fs_devices->lowest_bdev;
- fs_info->sb_buffer->dev_bytenr = sb_bytenr;
- ret = read_extent_from_disk(fs_info->sb_buffer);
- BUG_ON(ret);
- btrfs_set_buffer_uptodate(fs_info->sb_buffer);
-
- read_extent_buffer(fs_info->sb_buffer, &fs_info->super_copy, 0,
- sizeof(fs_info->super_copy));
- read_extent_buffer(fs_info->sb_buffer, fs_info->fsid,
- (unsigned long)btrfs_super_fsid(fs_info->sb_buffer),
- BTRFS_FSID_SIZE);
+ if (writes)
+ ret = btrfs_open_devices(fs_devices, O_RDWR);
+ else
+ ret = btrfs_open_devices(fs_devices, O_RDONLY);
+ if (ret)
+ goto out_cleanup;
+ fs_info->super_bytenr = sb_bytenr;
disk_super = &fs_info->super_copy;
- if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC,
- sizeof(disk_super->magic))) {
+ ret = btrfs_read_dev_super(fs_devices->latest_bdev,
+ disk_super, sb_bytenr);
+ if (ret) {
printk("No valid btrfs found\n");
- BUG_ON(1);
+ goto out_devices;
+ }
+
+ memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
+
+
+ features = btrfs_super_incompat_flags(disk_super) &
+ ~BTRFS_FEATURE_INCOMPAT_SUPP;
+ if (features) {
+ printk("couldn't open because of unsupported "
+ "option features (%Lx).\n",
+ (unsigned long long)features);
+ goto out_devices;
}
+
+ features = btrfs_super_incompat_flags(disk_super);
+ if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
+ features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
+ btrfs_set_super_incompat_flags(disk_super, features);
+ }
+
+ features = btrfs_super_compat_ro_flags(disk_super) &
+ ~BTRFS_FEATURE_COMPAT_RO_SUPP;
+ if (writes && features) {
+ printk("couldn't open RDWR because of unsupported "
+ "option features (%Lx).\n",
+ (unsigned long long)features);
+ goto out_devices;
+ }
+
nodesize = btrfs_super_nodesize(disk_super);
leafsize = btrfs_super_leafsize(disk_super);
sectorsize = btrfs_super_sectorsize(disk_super);
tree_root->sectorsize = sectorsize;
tree_root->stripesize = stripesize;
- ret = btrfs_read_super_device(tree_root, fs_info->sb_buffer);
- BUG_ON(ret);
ret = btrfs_read_sys_array(tree_root);
- BUG_ON(ret);
+ if (ret)
+ goto out_devices;
blocksize = btrfs_level_size(tree_root,
btrfs_super_chunk_root_level(disk_super));
+ generation = btrfs_super_chunk_root_generation(disk_super);
__setup_root(nodesize, leafsize, sectorsize, stripesize,
chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
chunk_root->node = read_tree_block(chunk_root,
btrfs_super_chunk_root(disk_super),
- blocksize);
+ blocksize, generation);
+ if (!extent_buffer_uptodate(chunk_root->node)) {
+ printk("Couldn't read chunk root\n");
+ goto out_devices;
+ }
- BUG_ON(!chunk_root->node);
+ read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid,
+ (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node),
+ BTRFS_UUID_SIZE);
- ret = btrfs_read_chunk_tree(chunk_root);
- BUG_ON(ret);
+ if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) {
+ ret = btrfs_read_chunk_tree(chunk_root);
+ if (ret)
+ goto out_failed;
+ }
blocksize = btrfs_level_size(tree_root,
btrfs_super_root_level(disk_super));
+ generation = btrfs_super_generation(disk_super);
+ if (!root_tree_bytenr)
+ root_tree_bytenr = btrfs_super_root(disk_super);
tree_root->node = read_tree_block(tree_root,
- btrfs_super_root(disk_super),
- blocksize);
- BUG_ON(!tree_root->node);
+ root_tree_bytenr,
+ blocksize, generation);
+ if (!extent_buffer_uptodate(tree_root->node)) {
+ printk("Couldn't read tree root\n");
+ goto out_failed;
+ }
ret = find_and_setup_root(tree_root, fs_info,
BTRFS_EXTENT_TREE_OBJECTID, extent_root);
- BUG_ON(ret);
+ if (ret) {
+ printk("Couldn't setup extent tree\n");
+ goto out_failed;
+ }
extent_root->track_dirty = 1;
ret = find_and_setup_root(tree_root, fs_info,
BTRFS_DEV_TREE_OBJECTID, dev_root);
- BUG_ON(ret);
+ if (ret) {
+ printk("Couldn't setup device tree\n");
+ goto out_failed;
+ }
dev_root->track_dirty = 1;
ret = find_and_setup_root(tree_root, fs_info,
- BTRFS_FS_TREE_OBJECTID, root);
- BUG_ON(ret);
- root->ref_cows = 1;
- fs_info->generation = btrfs_super_generation(disk_super) + 1;
- btrfs_read_block_groups(root);
- return root;
+ BTRFS_CSUM_TREE_OBJECTID, csum_root);
+ if (ret) {
+ printk("Couldn't setup csum tree\n");
+ if (!partial)
+ goto out_failed;
+ }
+ csum_root->track_dirty = 1;
+
+ find_and_setup_log_root(tree_root, fs_info, disk_super);
+
+ fs_info->generation = generation;
+ fs_info->last_trans_committed = generation;
+ btrfs_read_block_groups(fs_info->tree_root);
+
+ key.objectid = BTRFS_FS_TREE_OBJECTID;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
+
+ if (!fs_info->fs_root)
+ goto out_failed;
+
+ fs_info->data_alloc_profile = (u64)-1;
+ fs_info->metadata_alloc_profile = (u64)-1;
+ fs_info->system_alloc_profile = fs_info->metadata_alloc_profile;
+
+ return fs_info;
+
+out_failed:
+ if (partial)
+ return fs_info;
+
+ if (fs_info->csum_root)
+ free_extent_buffer(fs_info->csum_root->node);
+ if (fs_info->dev_root)
+ free_extent_buffer(fs_info->dev_root->node);
+ if (fs_info->extent_root)
+ free_extent_buffer(fs_info->extent_root->node);
+ if (fs_info->tree_root)
+ free_extent_buffer(fs_info->tree_root->node);
+ if (fs_info->chunk_root)
+ free_extent_buffer(fs_info->chunk_root->node);
+out_devices:
+ close_all_devices(fs_info);
+out_cleanup:
+ extent_io_tree_cleanup(&fs_info->extent_cache);
+ extent_io_tree_cleanup(&fs_info->free_space_cache);
+ extent_io_tree_cleanup(&fs_info->block_group_cache);
+ extent_io_tree_cleanup(&fs_info->pinned_extents);
+ extent_io_tree_cleanup(&fs_info->pending_del);
+ extent_io_tree_cleanup(&fs_info->extent_ins);
+out:
+ free(tree_root);
+ free(extent_root);
+ free(chunk_root);
+ free(dev_root);
+ free(csum_root);
+ free(fs_info);
+ return NULL;
+}
+
+struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
+ u64 sb_bytenr, int writes,
+ int partial)
+{
+ int fp;
+ struct btrfs_fs_info *info;
+ int flags = O_CREAT | O_RDWR;
+
+ if (!writes)
+ flags = O_RDONLY;
+
+ fp = open(filename, flags, 0600);
+ if (fp < 0) {
+ fprintf (stderr, "Could not open %s\n", filename);
+ return NULL;
+ }
+ info = __open_ctree_fd(fp, filename, sb_bytenr, 0, writes, partial);
+ close(fp);
+ return info;
+}
+
+struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
+{
+ struct btrfs_fs_info *info;
+
+ info = open_ctree_fs_info(filename, sb_bytenr, writes, 0);
+ if (!info)
+ return NULL;
+ return info->fs_root;
+}
+
+struct btrfs_root *open_ctree_recovery(const char *filename, u64 sb_bytenr,
+ u64 root_tree_bytenr)
+{
+ int fp;
+ struct btrfs_fs_info *info;
+
+
+ fp = open(filename, O_RDONLY);
+ if (fp < 0) {
+ fprintf (stderr, "Could not open %s\n", filename);
+ return NULL;
+ }
+ info = __open_ctree_fd(fp, filename, sb_bytenr,
+ root_tree_bytenr, 0, 0);
+ close(fp);
+
+ if (!info)
+ return NULL;
+ return info->fs_root;
+}
+
+struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
+ int writes)
+{
+ struct btrfs_fs_info *info;
+ info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0);
+ if (!info)
+ return NULL;
+ return info->fs_root;
+}
+
+int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr)
+{
+ u8 fsid[BTRFS_FSID_SIZE];
+ int fsid_is_initialized = 0;
+ struct btrfs_super_block buf;
+ int i;
+ int ret;
+ u64 transid = 0;
+ u64 bytenr;
+
+ if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) {
+ ret = pread64(fd, &buf, sizeof(buf), sb_bytenr);
+ if (ret < sizeof(buf))
+ return -1;
+
+ if (btrfs_super_bytenr(&buf) != sb_bytenr ||
+ buf.magic != cpu_to_le64(BTRFS_MAGIC))
+ return -1;
+
+ memcpy(sb, &buf, sizeof(*sb));
+ return 0;
+ }
+
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ bytenr = btrfs_sb_offset(i);
+ ret = pread64(fd, &buf, sizeof(buf), bytenr);
+ if (ret < sizeof(buf))
+ break;
+
+ if (btrfs_super_bytenr(&buf) != bytenr )
+ continue;
+ /* if magic is NULL, the device was removed */
+ if (buf.magic == 0 && i == 0)
+ return -1;
+ if (buf.magic != cpu_to_le64(BTRFS_MAGIC))
+ continue;
+
+ if (!fsid_is_initialized) {
+ memcpy(fsid, buf.fsid, sizeof(fsid));
+ fsid_is_initialized = 1;
+ } else if (memcmp(fsid, buf.fsid, sizeof(fsid))) {
+ /*
+ * the superblocks (the original one and
+ * its backups) contain data of different
+ * filesystems -> the super cannot be trusted
+ */
+ continue;
+ }
+
+ if (btrfs_super_generation(&buf) > transid) {
+ memcpy(sb, &buf, sizeof(*sb));
+ transid = btrfs_super_generation(&buf);
+ }
+ }
+
+ return transid > 0 ? 0 : -1;
+}
+
+int write_dev_supers(struct btrfs_root *root, struct btrfs_super_block *sb,
+ struct btrfs_device *device)
+{
+ u64 bytenr;
+ u32 crc;
+ int i, ret;
+ void *buf;
+
+ buf = calloc(1, BTRFS_SUPER_INFO_SIZE);
+ BUG_ON(!buf);
+
+ if (root->fs_info->super_bytenr != BTRFS_SUPER_INFO_OFFSET) {
+ btrfs_set_super_bytenr(sb, root->fs_info->super_bytenr);
+ crc = ~(u32)0;
+ crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, (char *)&sb->csum[0]);
+
+ memcpy(buf, sb, sizeof(*sb));
+ ret = pwrite64(device->fd, buf, BTRFS_SUPER_INFO_SIZE,
+ root->fs_info->super_bytenr);
+ BUG_ON(ret != BTRFS_SUPER_INFO_SIZE);
+ goto out;
+ }
+
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ bytenr = btrfs_sb_offset(i);
+ if (bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
+ break;
+
+ btrfs_set_super_bytenr(sb, bytenr);
+
+ crc = ~(u32)0;
+ crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+ btrfs_csum_final(crc, (char *)&sb->csum[0]);
+
+ memcpy(buf, sb, sizeof(*sb));
+ ret = pwrite64(device->fd, buf, BTRFS_SUPER_INFO_SIZE, bytenr);
+ BUG_ON(ret != BTRFS_SUPER_INFO_SIZE);
+ }
+out:
+ free(buf);
+ return 0;
+}
+
+int write_all_supers(struct btrfs_root *root)
+{
+ struct list_head *cur;
+ struct list_head *head = &root->fs_info->fs_devices->devices;
+ struct btrfs_device *dev;
+ struct btrfs_super_block *sb;
+ struct btrfs_dev_item *dev_item;
+ int ret;
+ u64 flags;
+
+ sb = &root->fs_info->super_copy;
+ dev_item = &sb->dev_item;
+ list_for_each(cur, head) {
+ dev = list_entry(cur, struct btrfs_device, dev_list);
+ if (!dev->writeable)
+ continue;
+
+ btrfs_set_stack_device_generation(dev_item, 0);
+ btrfs_set_stack_device_type(dev_item, dev->type);
+ btrfs_set_stack_device_id(dev_item, dev->devid);
+ btrfs_set_stack_device_total_bytes(dev_item, dev->total_bytes);
+ btrfs_set_stack_device_bytes_used(dev_item, dev->bytes_used);
+ btrfs_set_stack_device_io_align(dev_item, dev->io_align);
+ btrfs_set_stack_device_io_width(dev_item, dev->io_width);
+ btrfs_set_stack_device_sector_size(dev_item, dev->sector_size);
+ memcpy(dev_item->uuid, dev->uuid, BTRFS_UUID_SIZE);
+ memcpy(dev_item->fsid, dev->fs_devices->fsid, BTRFS_UUID_SIZE);
+
+ flags = btrfs_super_flags(sb);
+ btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN);
+
+ ret = write_dev_supers(root, sb, dev);
+ BUG_ON(ret);
+ }
+ return 0;
}
int write_ctree_super(struct btrfs_trans_handle *trans,
int ret;
struct btrfs_root *tree_root = root->fs_info->tree_root;
struct btrfs_root *chunk_root = root->fs_info->chunk_root;
+
+ if (root->fs_info->readonly)
+ return 0;
+
btrfs_set_super_generation(&root->fs_info->super_copy,
trans->transid);
btrfs_set_super_root(&root->fs_info->super_copy,
chunk_root->node->start);
btrfs_set_super_chunk_root_level(&root->fs_info->super_copy,
btrfs_header_level(chunk_root->node));
- write_extent_buffer(root->fs_info->sb_buffer,
- &root->fs_info->super_copy, 0,
- sizeof(root->fs_info->super_copy));
- ret = write_tree_block(trans, root, root->fs_info->sb_buffer);
+ btrfs_set_super_chunk_root_generation(&root->fs_info->super_copy,
+ btrfs_header_generation(chunk_root->node));
+
+ ret = write_all_supers(root);
if (ret)
fprintf(stderr, "failed to write new super block err %d\n", ret);
return ret;
list = &fs_info->fs_devices->devices;
list_for_each(next, list) {
device = list_entry(next, struct btrfs_device, dev_list);
- // close(device->fd);
+ if (device->fd) {
+ fsync(device->fd);
+ posix_fadvise(device->fd, 0, 0, POSIX_FADV_DONTNEED);
+ }
+ close(device->fd);
}
return 0;
}
struct btrfs_trans_handle *trans;
struct btrfs_fs_info *fs_info = root->fs_info;
- trans = btrfs_start_transaction(root, 1);
- btrfs_commit_transaction(trans, root);
- trans = btrfs_start_transaction(root, 1);
- ret = commit_tree_roots(trans, root->fs_info);
- BUG_ON(ret);
- ret = __commit_transaction(trans, root);
- BUG_ON(ret);
- write_ctree_super(trans, root);
- btrfs_free_transaction(root, trans);
- btrfs_free_block_groups(root->fs_info);
- close(root->fs_info->fp);
- if (root->node)
- free_extent_buffer(root->node);
- if (root->fs_info->extent_root->node)
- free_extent_buffer(root->fs_info->extent_root->node);
- if (root->fs_info->tree_root->node)
- free_extent_buffer(root->fs_info->tree_root->node);
- free_extent_buffer(root->commit_root);
- free_extent_buffer(root->fs_info->sb_buffer);
-
- if (root->fs_info->chunk_root->node);
- free_extent_buffer(root->fs_info->chunk_root->node);
-
- if (root->fs_info->dev_root->node);
- free_extent_buffer(root->fs_info->dev_root->node);
+ if (fs_info->last_trans_committed !=
+ fs_info->generation) {
+ trans = btrfs_start_transaction(root, 1);
+ btrfs_commit_transaction(trans, root);
+ trans = btrfs_start_transaction(root, 1);
+ ret = commit_tree_roots(trans, fs_info);
+ BUG_ON(ret);
+ ret = __commit_transaction(trans, root);
+ BUG_ON(ret);
+ write_ctree_super(trans, root);
+ btrfs_free_transaction(root, trans);
+ }
+ btrfs_free_block_groups(fs_info);
+
+ free_fs_roots(fs_info);
+
+ if (fs_info->extent_root->node)
+ free_extent_buffer(fs_info->extent_root->node);
+ if (fs_info->tree_root->node)
+ free_extent_buffer(fs_info->tree_root->node);
+ if (fs_info->chunk_root->node)
+ free_extent_buffer(fs_info->chunk_root->node);
+ if (fs_info->dev_root->node)
+ free_extent_buffer(fs_info->dev_root->node);
+ if (fs_info->csum_root->node)
+ free_extent_buffer(fs_info->csum_root->node);
+
+ if (fs_info->log_root_tree) {
+ if (fs_info->log_root_tree->node)
+ free_extent_buffer(fs_info->log_root_tree->node);
+ free(fs_info->log_root_tree);
+ }
- close_all_devices(root->fs_info);
+ close_all_devices(fs_info);
extent_io_tree_cleanup(&fs_info->extent_cache);
extent_io_tree_cleanup(&fs_info->free_space_cache);
extent_io_tree_cleanup(&fs_info->block_group_cache);
free(fs_info->tree_root);
free(fs_info->extent_root);
- free(fs_info->fs_root);
free(fs_info->chunk_root);
free(fs_info->dev_root);
+ free(fs_info->csum_root);
free(fs_info);
return 0;
set_extent_buffer_dirty(eb);
}
-int btrfs_buffer_uptodate(struct extent_buffer *eb)
+int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
{
- return extent_buffer_uptodate(eb);
+ int ret;
+
+ ret = extent_buffer_uptodate(buf);
+ if (!ret)
+ return ret;
+
+ ret = verify_parent_transid(buf->tree, buf, parent_transid, 1);
+ return !ret;
}
int btrfs_set_buffer_uptodate(struct extent_buffer *eb)