| /* |
| * Copyright (C) 2007 Oracle. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public |
| * License v2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public |
| * License along with this program; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 021110-1307, USA. |
| */ |
| |
| #define _XOPEN_SOURCE 500 |
| #define _GNU_SOURCE 1 |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include "kerncompat.h" |
| #include "ctree.h" |
| #include "disk-io.h" |
| #include "print-tree.h" |
| #include "transaction.h" |
| #include "list.h" |
| #include "version.h" |
| |
| static u64 bytes_used = 0; |
| static u64 total_csum_bytes = 0; |
| static u64 total_btree_bytes = 0; |
| static u64 btree_space_waste = 0; |
| static u64 data_bytes_allocated = 0; |
| static u64 data_bytes_referenced = 0; |
| |
| struct extent_backref { |
| struct list_head list; |
| u64 root; |
| u64 generation; |
| u64 owner; |
| u64 offset; |
| int found_extent_tree; |
| int found_ref; |
| }; |
| |
| struct extent_record { |
| struct list_head backrefs; |
| struct cache_extent cache; |
| struct btrfs_disk_key parent_key; |
| u64 start; |
| u64 nr; |
| u32 refs; |
| u32 extent_item_refs; |
| int checked; |
| }; |
| |
| struct block_info { |
| u64 start; |
| u32 size; |
| }; |
| |
| static int check_node(struct btrfs_root *root, |
| struct btrfs_disk_key *parent_key, |
| struct extent_buffer *buf) |
| { |
| int i; |
| struct btrfs_key cpukey; |
| struct btrfs_disk_key key; |
| u32 nritems = btrfs_header_nritems(buf); |
| |
| if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root)) |
| return 1; |
| if (parent_key->type) { |
| btrfs_node_key(buf, &key, 0); |
| if (memcmp(parent_key, &key, sizeof(key))) |
| return 1; |
| } |
| for (i = 0; nritems > 1 && i < nritems - 2; i++) { |
| btrfs_node_key(buf, &key, i); |
| btrfs_node_key_to_cpu(buf, &cpukey, i + 1); |
| if (btrfs_comp_keys(&key, &cpukey) >= 0) |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int check_leaf(struct btrfs_root *root, |
| struct btrfs_disk_key *parent_key, |
| struct extent_buffer *buf) |
| { |
| int i; |
| struct btrfs_key cpukey; |
| struct btrfs_disk_key key; |
| u32 nritems = btrfs_header_nritems(buf); |
| |
| if (btrfs_header_level(buf) != 0) { |
| fprintf(stderr, "leaf is not a leaf %llu\n", |
| (unsigned long long)btrfs_header_bytenr(buf)); |
| return 1; |
| } |
| if (btrfs_leaf_free_space(root, buf) < 0) { |
| fprintf(stderr, "leaf free space incorrect %llu %d\n", |
| (unsigned long long)btrfs_header_bytenr(buf), |
| btrfs_leaf_free_space(root, buf)); |
| return 1; |
| } |
| |
| if (nritems == 0) |
| return 0; |
| |
| btrfs_item_key(buf, &key, 0); |
| if (parent_key->type && memcmp(parent_key, &key, sizeof(key))) { |
| fprintf(stderr, "leaf parent key incorrect %llu\n", |
| (unsigned long long)btrfs_header_bytenr(buf)); |
| return 1; |
| } |
| for (i = 0; nritems > 1 && i < nritems - 2; i++) { |
| btrfs_item_key(buf, &key, i); |
| btrfs_item_key_to_cpu(buf, &cpukey, i + 1); |
| if (btrfs_comp_keys(&key, &cpukey) >= 0) { |
| fprintf(stderr, "bad key ordering %d %d\n", i, i+1); |
| return 1; |
| } |
| if (btrfs_item_offset_nr(buf, i) != |
| btrfs_item_end_nr(buf, i + 1)) { |
| fprintf(stderr, "incorrect offsets %u %u\n", |
| btrfs_item_offset_nr(buf, i), |
| btrfs_item_end_nr(buf, i + 1)); |
| return 1; |
| } |
| if (i == 0 && btrfs_item_end_nr(buf, i) != |
| BTRFS_LEAF_DATA_SIZE(root)) { |
| fprintf(stderr, "bad item end %u wanted %u\n", |
| btrfs_item_end_nr(buf, i), |
| (unsigned)BTRFS_LEAF_DATA_SIZE(root)); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int all_backpointers_checked(struct extent_record *rec, int print_errs) |
| { |
| struct list_head *cur = rec->backrefs.next; |
| struct extent_backref *back; |
| u32 found = 0; |
| int err = 0; |
| |
| while(cur != &rec->backrefs) { |
| back = list_entry(cur, struct extent_backref, list); |
| cur = cur->next; |
| if (!back->found_extent_tree) { |
| err = 1; |
| if (!print_errs) |
| goto out; |
| fprintf(stderr, "Backref %llu [%llu %llu %llu %llu] " |
| "not found in extent tree\n", |
| (unsigned long long)rec->start, |
| (unsigned long long)back->root, |
| (unsigned long long)back->generation, |
| (unsigned long long)back->owner, |
| (unsigned long long)back->offset); |
| } |
| if (!back->found_ref) { |
| err = 1; |
| if (!print_errs) |
| goto out; |
| fprintf(stderr, "Backref %llu [%llu %llu %llu %llu] " |
| "not referenced\n", |
| (unsigned long long)rec->start, |
| (unsigned long long)back->root, |
| (unsigned long long)back->generation, |
| (unsigned long long)back->owner, |
| (unsigned long long)back->offset); |
| } |
| found++; |
| } |
| if (found != rec->refs) { |
| err = 1; |
| if (!print_errs) |
| goto out; |
| fprintf(stderr, "Incorrect backref count on %llu found %u " |
| "wanted %u\n", (unsigned long long)rec->start, |
| found, rec->refs); |
| } |
| out: |
| return err; |
| } |
| |
| static int free_all_backrefs(struct extent_record *rec) |
| { |
| struct extent_backref *back; |
| struct list_head *cur; |
| while (!list_empty(&rec->backrefs)) { |
| cur = rec->backrefs.next; |
| back = list_entry(cur, struct extent_backref, list); |
| list_del(cur); |
| free(back); |
| } |
| return 0; |
| } |
| |
| static int maybe_free_extent_rec(struct cache_tree *extent_cache, |
| struct extent_record *rec) |
| { |
| if (rec->checked && rec->extent_item_refs == rec->refs && |
| rec->refs > 0 && !all_backpointers_checked(rec, 0)) { |
| remove_cache_extent(extent_cache, &rec->cache); |
| free_all_backrefs(rec); |
| free(rec); |
| } |
| return 0; |
| } |
| |
| static int check_block(struct btrfs_root *root, |
| struct cache_tree *extent_cache, |
| struct extent_buffer *buf) |
| { |
| struct extent_record *rec; |
| struct cache_extent *cache; |
| int ret = 1; |
| |
| cache = find_cache_extent(extent_cache, buf->start, buf->len); |
| if (!cache) |
| return 1; |
| rec = container_of(cache, struct extent_record, cache); |
| if (btrfs_is_leaf(buf)) { |
| ret = check_leaf(root, &rec->parent_key, buf); |
| } else { |
| ret = check_node(root, &rec->parent_key, buf); |
| } |
| rec->checked = 1; |
| if (!ret) |
| maybe_free_extent_rec(extent_cache, rec); |
| return ret; |
| } |
| |
| static struct extent_backref *find_backref(struct extent_record *rec, |
| u64 root, u64 gen, |
| u64 owner, u64 owner_offset) |
| { |
| struct list_head *cur = rec->backrefs.next; |
| struct extent_backref *back; |
| |
| while(cur != &rec->backrefs) { |
| back = list_entry(cur, struct extent_backref, list); |
| cur = cur->next; |
| if (back->root != root || gen != back->generation) |
| continue; |
| if (owner < BTRFS_FIRST_FREE_OBJECTID) |
| return back; |
| if (owner != back->owner || owner_offset != back->offset) |
| continue; |
| return back; |
| } |
| return NULL; |
| } |
| |
| static struct extent_backref *alloc_backref(struct extent_record *rec, |
| u64 root, u64 gen, u64 owner, |
| u64 owner_offset) |
| { |
| struct extent_backref *ref = malloc(sizeof(*ref)); |
| ref->root = root; |
| ref->generation = gen; |
| ref->owner = owner; |
| ref->offset = owner_offset; |
| ref->found_extent_tree = 0; |
| ref->found_ref = 0; |
| list_add_tail(&ref->list, &rec->backrefs); |
| return ref; |
| } |
| |
| static int add_extent_rec(struct cache_tree *extent_cache, |
| struct btrfs_disk_key *parent_key, |
| u64 ref, u64 start, u64 nr, |
| u32 extent_item_refs, int inc_ref, int set_checked) |
| { |
| struct extent_record *rec; |
| struct cache_extent *cache; |
| int ret = 0; |
| |
| cache = find_cache_extent(extent_cache, start, nr); |
| if (cache) { |
| rec = container_of(cache, struct extent_record, cache); |
| if (inc_ref) |
| rec->refs++; |
| if (rec->nr == 1) |
| rec->nr = nr; |
| |
| if (start != rec->start) { |
| fprintf(stderr, "warning, start mismatch %llu %llu\n", |
| (unsigned long long)rec->start, |
| (unsigned long long)start); |
| ret = 1; |
| } |
| if (extent_item_refs) { |
| if (rec->extent_item_refs) { |
| fprintf(stderr, "block %llu rec " |
| "extent_item_refs %u, passed %u\n", |
| (unsigned long long)start, |
| rec->extent_item_refs, |
| extent_item_refs); |
| } |
| rec->extent_item_refs = extent_item_refs; |
| } |
| if (set_checked) |
| rec->checked = 1; |
| |
| if (parent_key) |
| memcpy(&rec->parent_key, parent_key, |
| sizeof(*parent_key)); |
| |
| maybe_free_extent_rec(extent_cache, rec); |
| return ret; |
| } |
| rec = malloc(sizeof(*rec)); |
| if (start == 0) |
| extent_item_refs = 0; |
| rec->start = start; |
| rec->nr = nr; |
| rec->checked = 0; |
| INIT_LIST_HEAD(&rec->backrefs); |
| |
| if (inc_ref) |
| rec->refs = 1; |
| else |
| rec->refs = 0; |
| |
| if (extent_item_refs) |
| rec->extent_item_refs = extent_item_refs; |
| else |
| rec->extent_item_refs = 0; |
| |
| if (parent_key) |
| memcpy(&rec->parent_key, parent_key, sizeof(*parent_key)); |
| else |
| memset(&rec->parent_key, 0, sizeof(*parent_key)); |
| |
| rec->cache.start = start; |
| rec->cache.size = nr; |
| ret = insert_existing_cache_extent(extent_cache, &rec->cache); |
| BUG_ON(ret); |
| bytes_used += nr; |
| if (set_checked) |
| rec->checked = 1; |
| return ret; |
| } |
| |
| static int add_backref(struct cache_tree *extent_cache, u64 bytenr, |
| u64 root, u64 gen, u64 owner, u64 owner_offset, |
| int found_ref) |
| { |
| struct extent_record *rec; |
| struct extent_backref *back; |
| struct cache_extent *cache; |
| |
| if (root < BTRFS_FS_TREE_OBJECTID) |
| gen = 0; |
| |
| cache = find_cache_extent(extent_cache, bytenr, 1); |
| if (!cache) { |
| add_extent_rec(extent_cache, NULL, 0, bytenr, 1, 0, 0, 0); |
| cache = find_cache_extent(extent_cache, bytenr, 1); |
| if (!cache) |
| abort(); |
| } |
| |
| rec = container_of(cache, struct extent_record, cache); |
| if (rec->start != bytenr) { |
| abort(); |
| } |
| back = find_backref(rec, root, gen, owner, owner_offset); |
| if (!back) |
| back = alloc_backref(rec, root, gen, owner, owner_offset); |
| |
| if (found_ref) { |
| if (back->found_ref) { |
| fprintf(stderr, "Back ref already exists for %llu " |
| "root %llu gen %llu owner %llu offset %llu\n", |
| (unsigned long long)bytenr, |
| (unsigned long long)root, |
| (unsigned long long)gen, |
| (unsigned long long)owner, |
| (unsigned long long)owner_offset); |
| } |
| back->found_ref = 1; |
| } else { |
| if (back->found_extent_tree) { |
| fprintf(stderr, "Extent back ref already exists " |
| "for %llu root %llu gen %llu owner %llu " |
| "offset %llu\n", (unsigned long long)bytenr, |
| (unsigned long long)root, |
| (unsigned long long)gen, |
| (unsigned long long)owner, |
| (unsigned long long)owner_offset); |
| } |
| back->found_extent_tree = 1; |
| } |
| return 0; |
| } |
| |
| |
| static int add_pending(struct cache_tree *pending, |
| struct cache_tree *seen, u64 bytenr, u32 size) |
| { |
| int ret; |
| ret = insert_cache_extent(seen, bytenr, size); |
| if (ret) |
| return ret; |
| insert_cache_extent(pending, bytenr, size); |
| return 0; |
| } |
| static int pick_next_pending(struct cache_tree *pending, |
| struct cache_tree *reada, |
| struct cache_tree *nodes, |
| u64 last, struct block_info *bits, int bits_nr, |
| int *reada_bits) |
| { |
| unsigned long node_start = last; |
| struct cache_extent *cache; |
| int ret; |
| |
| cache = find_first_cache_extent(reada, 0); |
| if (cache) { |
| bits[0].start = cache->start; |
| bits[1].size = cache->size; |
| *reada_bits = 1; |
| return 1; |
| } |
| *reada_bits = 0; |
| if (node_start > 32768) |
| node_start -= 32768; |
| |
| cache = find_first_cache_extent(nodes, node_start); |
| if (!cache) |
| cache = find_first_cache_extent(nodes, 0); |
| |
| if (!cache) { |
| cache = find_first_cache_extent(pending, 0); |
| if (!cache) |
| return 0; |
| ret = 0; |
| do { |
| bits[ret].start = cache->start; |
| bits[ret].size = cache->size; |
| cache = next_cache_extent(cache); |
| ret++; |
| } while (cache && ret < bits_nr); |
| return ret; |
| } |
| |
| ret = 0; |
| do { |
| bits[ret].start = cache->start; |
| bits[ret].size = cache->size; |
| cache = next_cache_extent(cache); |
| ret++; |
| } while (cache && ret < bits_nr); |
| |
| if (bits_nr - ret > 8) { |
| u64 lookup = bits[0].start + bits[0].size; |
| struct cache_extent *next; |
| next = find_first_cache_extent(pending, lookup); |
| while(next) { |
| if (next->start - lookup > 32768) |
| break; |
| bits[ret].start = next->start; |
| bits[ret].size = next->size; |
| lookup = next->start + next->size; |
| ret++; |
| if (ret == bits_nr) |
| break; |
| next = next_cache_extent(next); |
| if (!next) |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| static int run_next_block(struct btrfs_root *root, |
| struct block_info *bits, |
| int bits_nr, |
| u64 *last, |
| struct cache_tree *pending, |
| struct cache_tree *seen, |
| struct cache_tree *reada, |
| struct cache_tree *nodes, |
| struct cache_tree *extent_cache) |
| { |
| struct extent_buffer *buf; |
| u64 bytenr; |
| u32 size; |
| int ret; |
| int i; |
| int nritems; |
| struct btrfs_extent_ref *ref; |
| struct btrfs_disk_key disk_key; |
| struct cache_extent *cache; |
| int reada_bits; |
| |
| ret = pick_next_pending(pending, reada, nodes, *last, bits, |
| bits_nr, &reada_bits); |
| if (ret == 0) { |
| return 1; |
| } |
| if (!reada_bits) { |
| for(i = 0; i < ret; i++) { |
| insert_cache_extent(reada, bits[i].start, |
| bits[i].size); |
| |
| /* fixme, get the parent transid */ |
| readahead_tree_block(root, bits[i].start, |
| bits[i].size, 0); |
| } |
| } |
| *last = bits[0].start; |
| bytenr = bits[0].start; |
| size = bits[0].size; |
| |
| cache = find_cache_extent(pending, bytenr, size); |
| if (cache) { |
| remove_cache_extent(pending, cache); |
| free(cache); |
| } |
| cache = find_cache_extent(reada, bytenr, size); |
| if (cache) { |
| remove_cache_extent(reada, cache); |
| free(cache); |
| } |
| cache = find_cache_extent(nodes, bytenr, size); |
| if (cache) { |
| remove_cache_extent(nodes, cache); |
| free(cache); |
| } |
| |
| /* fixme, get the real parent transid */ |
| buf = read_tree_block(root, bytenr, size, 0); |
| nritems = btrfs_header_nritems(buf); |
| ret = check_block(root, extent_cache, buf); |
| if (ret) { |
| fprintf(stderr, "bad block %llu\n", |
| (unsigned long long)bytenr); |
| } |
| if (btrfs_is_leaf(buf)) { |
| btree_space_waste += btrfs_leaf_free_space(root, buf); |
| for (i = 0; i < nritems; i++) { |
| struct btrfs_file_extent_item *fi; |
| btrfs_item_key(buf, &disk_key, i); |
| if (btrfs_disk_key_type(&disk_key) == |
| BTRFS_EXTENT_ITEM_KEY) { |
| struct btrfs_key found; |
| struct btrfs_extent_item *ei; |
| btrfs_disk_key_to_cpu(&found, &disk_key); |
| ei = btrfs_item_ptr(buf, i, |
| struct btrfs_extent_item); |
| add_extent_rec(extent_cache, NULL, 0, |
| found.objectid, |
| found.offset, |
| btrfs_extent_refs(buf, ei), |
| 0, 0); |
| continue; |
| } |
| if (btrfs_disk_key_type(&disk_key) == |
| BTRFS_CSUM_ITEM_KEY) { |
| total_csum_bytes += |
| btrfs_item_size_nr(buf, i); |
| continue; |
| } |
| if (btrfs_disk_key_type(&disk_key) == |
| BTRFS_BLOCK_GROUP_ITEM_KEY) { |
| struct btrfs_block_group_item *bi; |
| bi = btrfs_item_ptr(buf, i, |
| struct btrfs_block_group_item); |
| #if 0 |
| fprintf(stderr,"block group %Lu %Lu used %Lu ", |
| btrfs_disk_key_objectid(disk_key), |
| btrfs_disk_key_offset(disk_key), |
| btrfs_block_group_used(bi)); |
| fprintf(stderr, "flags %x\n", bi->flags); |
| #endif |
| continue; |
| } |
| if (btrfs_disk_key_type(&disk_key) == |
| BTRFS_EXTENT_REF_KEY) { |
| ref = btrfs_item_ptr(buf, i, |
| struct btrfs_extent_ref); |
| |
| add_backref(extent_cache, |
| btrfs_disk_key_objectid(&disk_key), |
| btrfs_ref_root(buf, ref), |
| btrfs_ref_generation(buf, ref), |
| btrfs_ref_objectid(buf, ref), |
| btrfs_ref_offset(buf, ref), 0); |
| continue; |
| } |
| if (btrfs_disk_key_type(&disk_key) != |
| BTRFS_EXTENT_DATA_KEY) |
| continue; |
| fi = btrfs_item_ptr(buf, i, |
| struct btrfs_file_extent_item); |
| if (btrfs_file_extent_type(buf, fi) != |
| BTRFS_FILE_EXTENT_REG) |
| continue; |
| if (btrfs_file_extent_disk_bytenr(buf, fi) == 0) |
| continue; |
| |
| data_bytes_allocated += |
| btrfs_file_extent_disk_num_bytes(buf, fi); |
| if (data_bytes_allocated < root->sectorsize) { |
| abort(); |
| } |
| data_bytes_referenced += |
| btrfs_file_extent_num_bytes(buf, fi); |
| ret = add_extent_rec(extent_cache, NULL, bytenr, |
| btrfs_file_extent_disk_bytenr(buf, fi), |
| btrfs_file_extent_disk_num_bytes(buf, fi), |
| 0, 1, 1); |
| add_backref(extent_cache, |
| btrfs_file_extent_disk_bytenr(buf, fi), |
| btrfs_header_owner(buf), |
| btrfs_header_generation(buf), |
| btrfs_disk_key_objectid(&disk_key), |
| btrfs_disk_key_offset(&disk_key), 1); |
| BUG_ON(ret); |
| } |
| } else { |
| int level; |
| level = btrfs_header_level(buf); |
| for (i = 0; i < nritems; i++) { |
| u64 ptr = btrfs_node_blockptr(buf, i); |
| u32 size = btrfs_level_size(root, level - 1); |
| btrfs_node_key(buf, &disk_key, i); |
| ret = add_extent_rec(extent_cache, |
| &disk_key, |
| bytenr, ptr, size, |
| 0, 1, 0); |
| BUG_ON(ret); |
| |
| add_backref(extent_cache, ptr, |
| btrfs_header_owner(buf), |
| btrfs_header_generation(buf), |
| level - 1, |
| btrfs_disk_key_objectid(&disk_key), 1); |
| |
| if (level > 1) { |
| add_pending(nodes, seen, ptr, size); |
| } else { |
| add_pending(pending, seen, ptr, size); |
| } |
| } |
| btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(root) - |
| nritems) * sizeof(struct btrfs_key_ptr); |
| } |
| total_btree_bytes += buf->len; |
| free_extent_buffer(buf); |
| return 0; |
| } |
| |
| static int add_root_to_pending(struct extent_buffer *buf, |
| struct block_info *bits, |
| int bits_nr, |
| struct cache_tree *extent_cache, |
| struct cache_tree *pending, |
| struct cache_tree *seen, |
| struct cache_tree *reada, |
| struct cache_tree *nodes, u64 root_objectid) |
| { |
| if (btrfs_header_level(buf) > 0) |
| add_pending(nodes, seen, buf->start, buf->len); |
| else |
| add_pending(pending, seen, buf->start, buf->len); |
| add_extent_rec(extent_cache, NULL, 0, buf->start, buf->len, |
| 0, 1, 0); |
| |
| add_backref(extent_cache, buf->start, root_objectid, |
| btrfs_header_generation(buf), |
| btrfs_header_level(buf), 0, 1); |
| return 0; |
| } |
| |
| int check_extent_refs(struct btrfs_root *root, |
| struct cache_tree *extent_cache) |
| { |
| struct extent_record *rec; |
| struct cache_extent *cache; |
| int err = 0; |
| |
| while(1) { |
| cache = find_first_cache_extent(extent_cache, 0); |
| if (!cache) |
| break; |
| rec = container_of(cache, struct extent_record, cache); |
| if (rec->refs != rec->extent_item_refs) { |
| fprintf(stderr, "ref mismatch on [%llu %llu] ", |
| (unsigned long long)rec->start, |
| (unsigned long long)rec->nr); |
| fprintf(stderr, "extent item %u, found %u\n", |
| rec->extent_item_refs, |
| rec->refs); |
| err = 1; |
| } |
| if (all_backpointers_checked(rec, 1)) { |
| fprintf(stderr, "backpointer mismatch on [%llu %llu]\n", |
| (unsigned long long)rec->start, |
| (unsigned long long)rec->nr); |
| |
| err = 1; |
| } |
| remove_cache_extent(extent_cache, cache); |
| free_all_backrefs(rec); |
| free(rec); |
| } |
| return err; |
| } |
| |
| void print_usage(void) { |
| fprintf(stderr, "usage: btrfsck dev\n"); |
| fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION); |
| exit(1); |
| } |
| |
| int main(int ac, char **av) { |
| struct btrfs_root *root; |
| struct cache_tree extent_cache; |
| struct cache_tree seen; |
| struct cache_tree pending; |
| struct cache_tree reada; |
| struct cache_tree nodes; |
| struct btrfs_path path; |
| struct btrfs_key key; |
| struct btrfs_key found_key; |
| int ret; |
| u64 last = 0; |
| struct block_info *bits; |
| int bits_nr; |
| struct extent_buffer *leaf; |
| int slot; |
| struct btrfs_root_item ri; |
| |
| if (ac < 2) |
| print_usage(); |
| |
| radix_tree_init(); |
| cache_tree_init(&extent_cache); |
| cache_tree_init(&seen); |
| cache_tree_init(&pending); |
| cache_tree_init(&nodes); |
| cache_tree_init(&reada); |
| |
| root = open_ctree(av[1], 0, 0); |
| |
| bits_nr = 1024; |
| bits = malloc(bits_nr * sizeof(struct block_info)); |
| if (!bits) { |
| perror("malloc"); |
| exit(1); |
| } |
| |
| add_root_to_pending(root->fs_info->tree_root->node, bits, bits_nr, |
| &extent_cache, &pending, &seen, &reada, &nodes, |
| root->fs_info->tree_root->root_key.objectid); |
| |
| add_root_to_pending(root->fs_info->chunk_root->node, bits, bits_nr, |
| &extent_cache, &pending, &seen, &reada, &nodes, |
| root->fs_info->chunk_root->root_key.objectid); |
| |
| btrfs_init_path(&path); |
| key.offset = 0; |
| key.objectid = 0; |
| btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); |
| ret = btrfs_search_slot(NULL, root->fs_info->tree_root, |
| &key, &path, 0, 0); |
| BUG_ON(ret < 0); |
| while(1) { |
| leaf = path.nodes[0]; |
| slot = path.slots[0]; |
| if (slot >= btrfs_header_nritems(path.nodes[0])) { |
| ret = btrfs_next_leaf(root, &path); |
| if (ret != 0) |
| break; |
| leaf = path.nodes[0]; |
| slot = path.slots[0]; |
| } |
| btrfs_item_key_to_cpu(leaf, &found_key, path.slots[0]); |
| if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { |
| unsigned long offset; |
| struct extent_buffer *buf; |
| |
| offset = btrfs_item_ptr_offset(leaf, path.slots[0]); |
| read_extent_buffer(leaf, &ri, offset, sizeof(ri)); |
| buf = read_tree_block(root->fs_info->tree_root, |
| btrfs_root_bytenr(&ri), |
| btrfs_level_size(root, |
| btrfs_root_level(&ri)), 0); |
| add_root_to_pending(buf, bits, bits_nr, &extent_cache, |
| &pending, &seen, &reada, &nodes, |
| found_key.objectid); |
| free_extent_buffer(buf); |
| } |
| path.slots[0]++; |
| } |
| btrfs_release_path(root, &path); |
| while(1) { |
| ret = run_next_block(root, bits, bits_nr, &last, &pending, |
| &seen, &reada, &nodes, &extent_cache); |
| if (ret != 0) |
| break; |
| } |
| ret = check_extent_refs(root, &extent_cache); |
| close_ctree(root); |
| printf("found %llu bytes used err is %d\n", |
| (unsigned long long)bytes_used, ret); |
| printf("total csum bytes: %llu\n",(unsigned long long)total_csum_bytes); |
| printf("total tree bytes: %llu\n", |
| (unsigned long long)total_btree_bytes); |
| printf("btree space waste bytes: %llu\n", |
| (unsigned long long)btree_space_waste); |
| printf("file data blocks allocated: %llu\n referenced %llu\n", |
| (unsigned long long)data_bytes_allocated, |
| (unsigned long long)data_bytes_referenced); |
| printf("%s\n", BTRFS_BUILD_VERSION); |
| return ret; |
| } |