void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow) { int i; u32 nr; u32 size; struct btrfs_disk_key disk_key; struct btrfs_key key; if (!eb) return; nr = btrfs_header_nritems(eb); if (btrfs_is_leaf(eb)) { btrfs_print_leaf(root, eb); return; } printf("node %llu level %d items %d free %u generation %llu owner %llu\n", (unsigned long long)eb->start, btrfs_header_level(eb), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, (unsigned long long)btrfs_header_generation(eb), (unsigned long long)btrfs_header_owner(eb)); print_uuids(eb); fflush(stdout); size = btrfs_level_size(root, btrfs_header_level(eb) - 1); for (i = 0; i < nr; i++) { u64 blocknr = btrfs_node_blockptr(eb, i); btrfs_node_key(eb, &disk_key, i); btrfs_disk_key_to_cpu(&key, &disk_key); printf("\t"); btrfs_print_key(&disk_key); printf(" block %llu (%llu) gen %llu\n", (unsigned long long)blocknr, (unsigned long long)blocknr / size, (unsigned long long)btrfs_node_ptr_generation(eb, i)); fflush(stdout); } if (!follow) return; for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(eb, i), size, btrfs_node_ptr_generation(eb, i)); if (!next) { fprintf(stderr, "failed to read %llu in tree %llu\n", (unsigned long long)btrfs_node_blockptr(eb, i), (unsigned long long)btrfs_header_owner(eb)); continue; } if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) BUG(); if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) BUG(); btrfs_print_tree(root, next, 1); free_extent_buffer(next); } }
static int __inode_info(u64 inum, u64 ioff, u8 key_type, struct btrfs_root *fs_root, struct btrfs_path *path, struct btrfs_key *found_key) { int ret; struct btrfs_key key; struct extent_buffer *eb; key.type = key_type; key.objectid = inum; key.offset = ioff; ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0); if (ret < 0) return ret; eb = path->nodes[0]; if (ret && path->slots[0] >= btrfs_header_nritems(eb)) { ret = btrfs_next_leaf(fs_root, path); if (ret) return ret; eb = path->nodes[0]; } btrfs_item_key_to_cpu(eb, found_key, path->slots[0]); if (found_key->type != key.type || found_key->objectid != key.objectid) return 1; return 0; }
/* * helper for dropping snapshots. This walks back up the tree in the path * to find the first node higher up where we haven't yet gone through * all the slots */ static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int *level) { int i; int slot; int ret; for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { slot = path->slots[i]; if (slot < btrfs_header_nritems(&path->nodes[i]->node.header)- 1) { path->slots[i]++; *level = i; return 0; } else { ret = btrfs_free_extent(trans, root, path->nodes[*level]->blocknr, 1, 1); btrfs_block_release(root, path->nodes[*level]); path->nodes[*level] = NULL; *level = i + 1; BUG_ON(ret); } } return 1; }
static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) { int i; u32 nr; u32 size; if (!eb) return; if (btrfs_is_leaf(eb)) { btrfs_print_leaf(root, eb); return; } size = btrfs_level_size(root, btrfs_header_level(eb) - 1); nr = btrfs_header_nritems(eb); for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(eb, i), size, btrfs_node_ptr_generation(eb, i)); if (!extent_buffer_uptodate(next)) continue; if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) BUG(); if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) BUG(); print_extents(root, next); free_extent_buffer(next); } }
struct btrfs_dir_item * btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, u64 dirid, const char *name, int name_len) { struct extent_buffer *leaf; struct btrfs_dir_item *di; struct btrfs_key key; u32 nritems; int ret; key.objectid = dirid; key.type = BTRFS_DIR_INDEX_KEY; key.offset = 0; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) return ERR_PTR(ret); leaf = path->nodes[0]; nritems = btrfs_header_nritems(leaf); while (1) { if (path->slots[0] >= nritems) { ret = btrfs_next_leaf(root, path); if (ret < 0) return ERR_PTR(ret); if (ret > 0) break; leaf = path->nodes[0]; nritems = btrfs_header_nritems(leaf); continue; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY) break; di = btrfs_match_dir_item_name(root->fs_info, path, name, name_len); if (di) return di; path->slots[0]++; } return NULL; }
static int next_leaf(struct btrfs_root *root, struct btrfs_path *path) { int slot; int level = 1; int offset = 1; struct extent_buffer *c; struct extent_buffer *next = NULL; struct btrfs_fs_info *fs_info = root->fs_info; again: for (; level < BTRFS_MAX_LEVEL; level++) { if (path->nodes[level]) break; } if (level >= BTRFS_MAX_LEVEL) return 1; slot = path->slots[level] + 1; while(level < BTRFS_MAX_LEVEL) { if (!path->nodes[level]) return 1; slot = path->slots[level] + offset; c = path->nodes[level]; if (slot >= btrfs_header_nritems(c)) { level++; if (level == BTRFS_MAX_LEVEL) return 1; offset = 1; continue; } if (path->reada) reada_for_search(fs_info, path, level, slot, 0); next = read_node_slot(fs_info, c, slot); if (extent_buffer_uptodate(next)) break; offset++; } path->slots[level] = slot; while(1) { level--; c = path->nodes[level]; free_extent_buffer(c); path->nodes[level] = next; path->slots[level] = 0; if (!level) break; if (path->reada) reada_for_search(fs_info, path, level, 0, 0); next = read_node_slot(fs_info, next, 0); if (!extent_buffer_uptodate(next)) goto again; } return 0; }
int next_leaf(struct btrfs_root *root, struct btrfs_path *path) { int slot; int level = 1; struct extent_buffer *c; struct extent_buffer *next = NULL; for (; level < BTRFS_MAX_LEVEL; level++) { if (path->nodes[level]) break; } if (level == BTRFS_MAX_LEVEL) return 1; slot = path->slots[level] + 1; while(level < BTRFS_MAX_LEVEL) { if (!path->nodes[level]) return 1; slot = path->slots[level] + 1; c = path->nodes[level]; if (slot >= btrfs_header_nritems(c)) { level++; if (level == BTRFS_MAX_LEVEL) return 1; continue; } if (next) free_extent_buffer(next); if (path->reada) reada_for_search(root, path, level, slot, 0); next = read_node_slot(root, c, slot); break; } path->slots[level] = slot; while(1) { level--; c = path->nodes[level]; free_extent_buffer(c); path->nodes[level] = next; path->slots[level] = 0; if (!level) break; if (path->reada) reada_for_search(root, path, level, 0, 0); next = read_node_slot(root, next, 0); } return 0; }
int btrfs_find_orphan_roots(struct btrfs_root *tree_root) { struct extent_buffer *leaf; struct btrfs_path *path; struct btrfs_key key; int err = 0; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; key.objectid = BTRFS_ORPHAN_OBJECTID; key.type = BTRFS_ORPHAN_ITEM_KEY; key.offset = 0; while (1) { ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); if (ret < 0) { err = ret; break; } leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(tree_root, path); if (ret < 0) err = ret; if (ret != 0) break; leaf = path->nodes[0]; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); btrfs_release_path(tree_root, path); if (key.objectid != BTRFS_ORPHAN_OBJECTID || key.type != BTRFS_ORPHAN_ITEM_KEY) break; ret = btrfs_find_dead_roots(tree_root, key.offset); if (ret) { err = ret; break; } key.offset++; } btrfs_free_path(path); return err; }
/* * helper function for drop_snapshot, this walks down the tree dropping ref * counts as it goes. */ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int *level) { struct btrfs_buffer *next; struct btrfs_buffer *cur; u64 blocknr; int ret; u32 refs; ret = lookup_block_ref(trans, root, path->nodes[*level]->blocknr, &refs); BUG_ON(ret); if (refs > 1) goto out; /* * walk down to the last node level and free all the leaves */ while(*level > 0) { cur = path->nodes[*level]; if (path->slots[*level] >= btrfs_header_nritems(&cur->node.header)) break; blocknr = btrfs_node_blockptr(&cur->node, path->slots[*level]); ret = lookup_block_ref(trans, root, blocknr, &refs); if (refs != 1 || *level == 1) { path->slots[*level]++; ret = btrfs_free_extent(trans, root, blocknr, 1, 1); BUG_ON(ret); continue; } BUG_ON(ret); next = read_tree_block(root, blocknr); if (path->nodes[*level-1]) btrfs_block_release(root, path->nodes[*level-1]); path->nodes[*level-1] = next; *level = btrfs_header_level(&next->node.header); path->slots[*level] = 0; } out: ret = btrfs_free_extent(trans, root, path->nodes[*level]->blocknr, 1, 1); btrfs_block_release(root, path->nodes[*level]); path->nodes[*level] = NULL; *level += 1; BUG_ON(ret); return 0; }
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_buffer *buf) { u64 blocknr; int i; if (!root->ref_cows) return 0; if (btrfs_is_leaf(&buf->node)) return 0; for (i = 0; i < btrfs_header_nritems(&buf->node.header); i++) { blocknr = btrfs_node_blockptr(&buf->node, i); inc_block_ref(trans, root, blocknr); } return 0; }
void btrfs_print_tree(struct btrfs_fs_info *fs_info, struct extent_buffer *c) { int i; u32 nr; struct btrfs_key key; int level; if (!c) return; nr = btrfs_header_nritems(c); level = btrfs_header_level(c); if (level == 0) { btrfs_print_leaf(fs_info, c); return; } btrfs_info(fs_info, "node %llu level %d total ptrs %d free spc %u", btrfs_header_bytenr(c), level, nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); pr_info("\tkey %d (%llu %u %llu) block %llu\n", i, key.objectid, key.type, key.offset, btrfs_node_blockptr(c, i)); } for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(fs_info, btrfs_node_blockptr(c, i), btrfs_node_ptr_generation(c, i)); if (IS_ERR(next)) { continue; } else if (!extent_buffer_uptodate(next)) { free_extent_buffer(next); continue; } if (btrfs_is_leaf(next) && level != 1) BUG(); if (btrfs_header_level(next) != level - 1) BUG(); btrfs_print_tree(fs_info, next); free_extent_buffer(next); } }
void btrfs_print_tree(struct btrfs_root *root, struct btrfs_buffer *t) { unsigned int i; u32 nr; struct btrfs_node *c; if (!t) return; c = &t->node; nr = btrfs_header_nritems(&c->header); if (btrfs_is_leaf(c)) { btrfs_print_leaf(root, (struct btrfs_leaf *)c); return; } printf("node %llu level %d ptrs %d free %u generation %llu owner %llu\n", (u64)t->blocknr, btrfs_header_level(&c->header), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, (u64)btrfs_header_generation(&c->header), (u64)btrfs_header_owner(&c->header)); fflush(stdout); for (i = 0; i < nr; i++) { printf("\tkey %d (%llu %x %llu) block %llu\n", i, (u64)c->ptrs[i].key.objectid, c->ptrs[i].key.flags, (u64)c->ptrs[i].key.offset, (u64)btrfs_node_blockptr(c, i)); fflush(stdout); } for (i = 0; i < nr; i++) { struct btrfs_buffer *next_buf = read_tree_block(root, btrfs_node_blockptr(c, i)); struct btrfs_node *next = &next_buf->node; if (btrfs_is_leaf(next) && btrfs_header_level(&c->header) != 1) BUG(); if (btrfs_header_level(&next->header) != btrfs_header_level(&c->header) - 1) BUG(); btrfs_print_tree(root, next_buf); btrfs_block_release(root, next_buf); } }
/* * We can't use btrfs_next_item() in modify_free_space_bitmap() because * btrfs_next_leaf() doesn't get the path for writing. We can forgo the fancy * tree walking in btrfs_next_leaf() anyways because we know exactly what we're * looking for. */ static int free_space_next_bitmap(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *p) { struct btrfs_key key; if (p->slots[0] + 1 < btrfs_header_nritems(p->nodes[0])) { p->slots[0]++; return 0; } btrfs_item_key_to_cpu(p->nodes[0], &key, p->slots[0]); btrfs_release_path(p); key.objectid += key.offset; key.type = (u8)-1; key.offset = (u64)-1; return btrfs_search_prev_slot(trans, root, &key, p, 0, 1); }
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *c) { int i; u32 nr; struct btrfs_key key; int level; if (!c) return; nr = btrfs_header_nritems(c); level = btrfs_header_level(c); if (level == 0) { btrfs_print_leaf(root, c); return; } printk(KERN_INFO "node %llu level %d total ptrs %d free spc %u\n", (unsigned long long)btrfs_header_bytenr(c), level, nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); printk(KERN_INFO "\tkey %d (%llu %u %llu) block %llu\n", i, (unsigned long long)key.objectid, key.type, (unsigned long long)key.offset, (unsigned long long)btrfs_node_blockptr(c, i)); } for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(c, i), btrfs_level_size(root, level - 1), btrfs_node_ptr_generation(c, i)); if (btrfs_is_leaf(next) && level != 1) BUG(); if (btrfs_header_level(next) != level - 1) BUG(); btrfs_print_tree(root, next); free_extent_buffer(next); } }
static int clear_free_space_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_path *path; struct btrfs_key key; int nr; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; path->leave_spinning = 1; key.objectid = 0; key.type = 0; key.offset = 0; while (1) { ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret < 0) goto out; nr = btrfs_header_nritems(path->nodes[0]); if (!nr) break; path->slots[0] = 0; ret = btrfs_del_items(trans, root, path, 0, nr); if (ret) goto out; btrfs_release_path(path); } ret = 0; out: btrfs_free_path(path); return ret; }
/* * search forward for a root, starting with objectid 'search_start' * if a root key is found, the objectid we find is filled into 'found_objectid' * and 0 is returned. < 0 is returned on error, 1 if there is nothing * left in the tree. */ int btrfs_search_root(struct btrfs_root *root, u64 search_start, u64 *found_objectid) { struct btrfs_path *path; struct btrfs_key search_key; int ret; root = root->fs_info->tree_root; search_key.objectid = search_start; search_key.type = (u8)-1; search_key.offset = (u64)-1; path = btrfs_alloc_path(); BUG_ON(!path); again: ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); if (ret < 0) goto out; if (ret == 0) { ret = 1; goto out; } if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { ret = btrfs_next_leaf(root, path); if (ret) goto out; } btrfs_item_key_to_cpu(path->nodes[0], &search_key, path->slots[0]); if (search_key.type != BTRFS_ROOT_ITEM_KEY) { search_key.offset++; btrfs_release_path(root, path); goto again; } ret = 0; *found_objectid = search_key.objectid; out: btrfs_free_path(path); return ret; }
static int find_extent_in_eb(struct extent_buffer *eb, u64 wanted_disk_byte, u64 extent_item_pos, struct extent_inode_elem **eie) { u64 disk_byte; struct btrfs_key key; struct btrfs_file_extent_item *fi; int slot; int nritems; int extent_type; int ret; /* * from the shared data ref, we only have the leaf but we need * the key. thus, we must look into all items and see that we * find one (some) with a reference to our extent item. */ nritems = btrfs_header_nritems(eb); for (slot = 0; slot < nritems; ++slot) { btrfs_item_key_to_cpu(eb, &key, slot); if (key.type != BTRFS_EXTENT_DATA_KEY) continue; fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); extent_type = btrfs_file_extent_type(eb, fi); if (extent_type == BTRFS_FILE_EXTENT_INLINE) continue; /* don't skip BTRFS_FILE_EXTENT_PREALLOC, we can handle that */ disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); if (disk_byte != wanted_disk_byte) continue; ret = check_extent_in_eb(&key, eb, fi, extent_item_pos, eie); if (ret < 0) return ret; } return 0; }
int btrfs_csum_file_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 alloc_end, u64 bytenr, char *data, size_t len) { int ret; struct btrfs_key file_key; struct btrfs_key found_key; u64 next_offset = (u64)-1; int found_next = 0; struct btrfs_path *path; struct btrfs_csum_item *item; struct extent_buffer *leaf = NULL; u64 csum_offset; u32 csum_result = ~(u32)0; u32 nritems; u32 ins_size; u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); path = btrfs_alloc_path(); BUG_ON(!path); file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; file_key.offset = bytenr; file_key.type = BTRFS_EXTENT_CSUM_KEY; item = btrfs_lookup_csum(trans, root, path, bytenr, 1); if (!IS_ERR(item)) { leaf = path->nodes[0]; goto found; } ret = PTR_ERR(item); if (ret == -EFBIG) { u32 item_size; /* we found one, but it isn't big enough yet */ leaf = path->nodes[0]; item_size = btrfs_item_size_nr(leaf, path->slots[0]); if ((item_size / csum_size) >= MAX_CSUM_ITEMS(root, csum_size)) { /* already at max size, make a new one */ goto insert; } } else { int slot = path->slots[0] + 1; /* we didn't find a csum item, insert one */ nritems = btrfs_header_nritems(path->nodes[0]); if (path->slots[0] >= nritems - 1) { ret = btrfs_next_leaf(root, path); if (ret == 1) found_next = 1; if (ret != 0) goto insert; slot = 0; } btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || found_key.type != BTRFS_EXTENT_CSUM_KEY) { found_next = 1; goto insert; } next_offset = found_key.offset; found_next = 1; goto insert; } /* * at this point, we know the tree has an item, but it isn't big * enough yet to put our csum in. Grow it */ btrfs_release_path(root, path); ret = btrfs_search_slot(trans, root, &file_key, path, csum_size, 1); if (ret < 0) goto fail; if (ret == 0) { BUG(); } if (path->slots[0] == 0) { goto insert; } path->slots[0]--; leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); csum_offset = (file_key.offset - found_key.offset) / root->sectorsize; if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || found_key.type != BTRFS_EXTENT_CSUM_KEY || csum_offset >= MAX_CSUM_ITEMS(root, csum_size)) { goto insert; } if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) / csum_size) { u32 diff = (csum_offset + 1) * csum_size; diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); if (diff != csum_size) goto insert; ret = btrfs_extend_item(trans, root, path, diff); BUG_ON(ret); goto csum; } insert: btrfs_release_path(root, path); csum_offset = 0; if (found_next) { u64 tmp = min(alloc_end, next_offset); tmp -= file_key.offset; tmp /= root->sectorsize; tmp = max((u64)1, tmp); tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root, csum_size)); ins_size = csum_size * tmp; } else { ins_size = csum_size; } ret = btrfs_insert_empty_item(trans, root, path, &file_key, ins_size); if (ret < 0) goto fail; if (ret != 0) { WARN_ON(1); goto fail; } csum: leaf = path->nodes[0]; item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); ret = 0; item = (struct btrfs_csum_item *)((unsigned char *)item + csum_offset * csum_size); found: csum_result = btrfs_csum_data(root, data, csum_result, len); btrfs_csum_final(csum_result, (char *)&csum_result); if (csum_result == 0) { printk("csum result is 0 for block %llu\n", (unsigned long long)bytenr); } write_extent_buffer(leaf, &csum_result, (unsigned long)item, csum_size); btrfs_mark_buffer_dirty(path->nodes[0]); fail: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
/* * this is very complex, but the basic idea is to drop all extents * in the range start - end. hint_block is filled in with a block number * that would be a good hint to the block allocator for this file. * * If an extent intersects the range but is not entirely inside the range * it is either truncated or split. Anything entirely inside the range * is deleted from the tree. */ int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode, u64 start, u64 end, u64 *hint_byte, int drop_cache) { struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_buffer *leaf; struct btrfs_file_extent_item *fi; struct btrfs_path *path; struct btrfs_key key; struct btrfs_key new_key; u64 search_start = start; u64 disk_bytenr = 0; u64 num_bytes = 0; u64 extent_offset = 0; u64 extent_end = 0; int del_nr = 0; int del_slot = 0; int extent_type; int recow; int ret; if (drop_cache) btrfs_drop_extent_cache(inode, start, end - 1, 0); path = btrfs_alloc_path(); if (!path) return -ENOMEM; while (1) { recow = 0; ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino, search_start, -1); if (ret < 0) break; if (ret > 0 && path->slots[0] > 0 && search_start == start) { leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &key, path->slots[0] - 1); if (key.objectid == inode->i_ino && key.type == BTRFS_EXTENT_DATA_KEY) path->slots[0]--; } ret = 0; next_slot: leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { BUG_ON(del_nr > 0); ret = btrfs_next_leaf(root, path); if (ret < 0) break; if (ret > 0) { ret = 0; break; } leaf = path->nodes[0]; recow = 1; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid > inode->i_ino || key.type > BTRFS_EXTENT_DATA_KEY || key.offset >= end) break; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); extent_type = btrfs_file_extent_type(leaf, fi); if (extent_type == BTRFS_FILE_EXTENT_REG || extent_type == BTRFS_FILE_EXTENT_PREALLOC) { disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi); extent_offset = btrfs_file_extent_offset(leaf, fi); extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { extent_end = key.offset + btrfs_file_extent_inline_len(leaf, fi); } else { WARN_ON(1); extent_end = search_start; } if (extent_end <= search_start) { path->slots[0]++; goto next_slot; } search_start = max(key.offset, start); if (recow) { btrfs_release_path(root, path); continue; } /* * | - range to drop - | * | -------- extent -------- | */ if (start > key.offset && end < extent_end) { BUG_ON(del_nr > 0); BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); memcpy(&new_key, &key, sizeof(new_key)); new_key.offset = start; ret = btrfs_duplicate_item(trans, root, path, &new_key); if (ret == -EAGAIN) { btrfs_release_path(root, path); continue; } if (ret < 0) break; leaf = path->nodes[0]; fi = btrfs_item_ptr(leaf, path->slots[0] - 1, struct btrfs_file_extent_item); btrfs_set_file_extent_num_bytes(leaf, fi, start - key.offset); fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); extent_offset += start - key.offset; btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - start); btrfs_mark_buffer_dirty(leaf); if (disk_bytenr > 0) { ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, root->root_key.objectid, new_key.objectid, start - extent_offset); BUG_ON(ret); *hint_byte = disk_bytenr; } key.offset = start; } /* * | ---- range to drop ----- | * | -------- extent -------- | */ if (start <= key.offset && end < extent_end) { BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); memcpy(&new_key, &key, sizeof(new_key)); new_key.offset = end; btrfs_set_item_key_safe(trans, root, path, &new_key); extent_offset += end - key.offset; btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - end); btrfs_mark_buffer_dirty(leaf); if (disk_bytenr > 0) { inode_sub_bytes(inode, end - key.offset); *hint_byte = disk_bytenr; } break; } search_start = extent_end; /* * | ---- range to drop ----- | * | -------- extent -------- | */ if (start > key.offset && end >= extent_end) { BUG_ON(del_nr > 0); BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); btrfs_set_file_extent_num_bytes(leaf, fi, start - key.offset); btrfs_mark_buffer_dirty(leaf); if (disk_bytenr > 0) { inode_sub_bytes(inode, extent_end - start); *hint_byte = disk_bytenr; } if (end == extent_end) break; path->slots[0]++; goto next_slot; } /* * | ---- range to drop ----- | * | ------ extent ------ | */ if (start <= key.offset && end >= extent_end) { if (del_nr == 0) { del_slot = path->slots[0]; del_nr = 1; } else { BUG_ON(del_slot + del_nr != path->slots[0]); del_nr++; } if (extent_type == BTRFS_FILE_EXTENT_INLINE) { inode_sub_bytes(inode, extent_end - key.offset); extent_end = ALIGN(extent_end, root->sectorsize); } else if (disk_bytenr > 0) { ret = btrfs_free_extent(trans, root, disk_bytenr, num_bytes, 0, root->root_key.objectid, key.objectid, key.offset - extent_offset); BUG_ON(ret); inode_sub_bytes(inode, extent_end - key.offset); *hint_byte = disk_bytenr; } if (end == extent_end) break; if (path->slots[0] + 1 < btrfs_header_nritems(leaf)) { path->slots[0]++; goto next_slot; } ret = btrfs_del_items(trans, root, path, del_slot, del_nr); BUG_ON(ret); del_nr = 0; del_slot = 0; btrfs_release_path(root, path); continue; } BUG_ON(1); }
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) { int i; char *str; struct btrfs_item *item; struct btrfs_dir_item *di; struct btrfs_inode_item *ii; struct btrfs_file_extent_item *fi; struct btrfs_block_group_item *bi; struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; struct btrfs_inode_ref *iref; struct btrfs_inode_extref *iref2; struct btrfs_dev_extent *dev_extent; struct btrfs_disk_key disk_key; struct btrfs_block_group_item bg_item; struct btrfs_dir_log_item *dlog; struct btrfs_qgroup_info_item *qg_info; struct btrfs_qgroup_limit_item *qg_limit; struct btrfs_qgroup_status_item *qg_status; u32 nr = btrfs_header_nritems(l); u64 objectid; u32 type; printf("leaf %llu items %d free space %d generation %llu owner %llu\n", (unsigned long long)btrfs_header_bytenr(l), nr, btrfs_leaf_free_space(root, l), (unsigned long long)btrfs_header_generation(l), (unsigned long long)btrfs_header_owner(l)); print_uuids(l); fflush(stdout); for (i = 0 ; i < nr ; i++) { item = btrfs_item_nr(l, i); btrfs_item_key(l, &disk_key, i); objectid = btrfs_disk_key_objectid(&disk_key); type = btrfs_disk_key_type(&disk_key); printf("\titem %d ", i); btrfs_print_key(&disk_key); printf(" itemoff %d itemsize %d\n", btrfs_item_offset(l, item), btrfs_item_size(l, item)); if (type == 0 && objectid == BTRFS_FREE_SPACE_OBJECTID) print_free_space_header(l, i); switch (type) { case BTRFS_INODE_ITEM_KEY: ii = btrfs_item_ptr(l, i, struct btrfs_inode_item); printf("\t\tinode generation %llu transid %llu size %llu block group %llu mode %o links %u\n", (unsigned long long)btrfs_inode_generation(l, ii), (unsigned long long)btrfs_inode_transid(l, ii), (unsigned long long)btrfs_inode_size(l, ii), (unsigned long long)btrfs_inode_block_group(l,ii), btrfs_inode_mode(l, ii), btrfs_inode_nlink(l, ii)); break; case BTRFS_INODE_REF_KEY: iref = btrfs_item_ptr(l, i, struct btrfs_inode_ref); print_inode_ref_item(l, item, iref); break; case BTRFS_INODE_EXTREF_KEY: iref2 = btrfs_item_ptr(l, i, struct btrfs_inode_extref); print_inode_extref_item(l, item, iref2); break; case BTRFS_DIR_ITEM_KEY: case BTRFS_DIR_INDEX_KEY: case BTRFS_XATTR_ITEM_KEY: di = btrfs_item_ptr(l, i, struct btrfs_dir_item); print_dir_item(l, item, di); break; case BTRFS_DIR_LOG_INDEX_KEY: case BTRFS_DIR_LOG_ITEM_KEY: dlog = btrfs_item_ptr(l, i, struct btrfs_dir_log_item); printf("\t\tdir log end %Lu\n", (unsigned long long)btrfs_dir_log_end(l, dlog)); break; case BTRFS_ORPHAN_ITEM_KEY: printf("\t\torphan item\n"); break; case BTRFS_ROOT_ITEM_KEY: print_root(l, i); break; case BTRFS_ROOT_REF_KEY: print_root_ref(l, i, "ref"); break; case BTRFS_ROOT_BACKREF_KEY: print_root_ref(l, i, "backref"); break; case BTRFS_EXTENT_ITEM_KEY: print_extent_item(l, i, 0); break; case BTRFS_METADATA_ITEM_KEY: print_extent_item(l, i, 1); break; case BTRFS_TREE_BLOCK_REF_KEY: printf("\t\ttree block backref\n"); break; case BTRFS_SHARED_BLOCK_REF_KEY: printf("\t\tshared block backref\n"); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = btrfs_item_ptr(l, i, struct btrfs_extent_data_ref); printf("\t\textent data backref root %llu " "objectid %llu offset %llu count %u\n", (unsigned long long)btrfs_extent_data_ref_root(l, dref), (unsigned long long)btrfs_extent_data_ref_objectid(l, dref), (unsigned long long)btrfs_extent_data_ref_offset(l, dref), btrfs_extent_data_ref_count(l, dref)); break; case BTRFS_SHARED_DATA_REF_KEY: sref = btrfs_item_ptr(l, i, struct btrfs_shared_data_ref); printf("\t\tshared data backref count %u\n", btrfs_shared_data_ref_count(l, sref)); break; case BTRFS_EXTENT_REF_V0_KEY: #ifdef BTRFS_COMPAT_EXTENT_TREE_V0 print_extent_ref_v0(l, i); #else BUG(); #endif break; case BTRFS_CSUM_ITEM_KEY: printf("\t\tcsum item\n"); break; case BTRFS_EXTENT_CSUM_KEY: printf("\t\textent csum item\n"); break; case BTRFS_EXTENT_DATA_KEY: fi = btrfs_item_ptr(l, i, struct btrfs_file_extent_item); print_file_extent_item(l, item, fi); break; case BTRFS_BLOCK_GROUP_ITEM_KEY: bi = btrfs_item_ptr(l, i, struct btrfs_block_group_item); read_extent_buffer(l, &bg_item, (unsigned long)bi, sizeof(bg_item)); printf("\t\tblock group used %llu chunk_objectid %llu flags %llu\n", (unsigned long long)btrfs_block_group_used(&bg_item), (unsigned long long)btrfs_block_group_chunk_objectid(&bg_item), (unsigned long long)btrfs_block_group_flags(&bg_item)); break; case BTRFS_CHUNK_ITEM_KEY: print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk)); break; case BTRFS_DEV_ITEM_KEY: print_dev_item(l, btrfs_item_ptr(l, i, struct btrfs_dev_item)); break; case BTRFS_DEV_EXTENT_KEY: dev_extent = btrfs_item_ptr(l, i, struct btrfs_dev_extent); printf("\t\tdev extent chunk_tree %llu\n" "\t\tchunk objectid %llu chunk offset %llu " "length %llu\n", (unsigned long long) btrfs_dev_extent_chunk_tree(l, dev_extent), (unsigned long long) btrfs_dev_extent_chunk_objectid(l, dev_extent), (unsigned long long) btrfs_dev_extent_chunk_offset(l, dev_extent), (unsigned long long) btrfs_dev_extent_length(l, dev_extent)); break; case BTRFS_QGROUP_STATUS_KEY: qg_status = btrfs_item_ptr(l, i, struct btrfs_qgroup_status_item); printf("\t\tversion %llu generation %llu flags %#llx " "scan %lld\n", (unsigned long long) btrfs_qgroup_status_version(l, qg_status), (unsigned long long) btrfs_qgroup_status_generation(l, qg_status), (unsigned long long) btrfs_qgroup_status_flags(l, qg_status), (unsigned long long) btrfs_qgroup_status_scan(l, qg_status)); break; case BTRFS_QGROUP_RELATION_KEY: break; case BTRFS_QGROUP_INFO_KEY: qg_info = btrfs_item_ptr(l, i, struct btrfs_qgroup_info_item); printf("\t\tgeneration %llu\n" "\t\treferenced %lld referenced compressed %lld\n" "\t\texclusive %lld exclusive compressed %lld\n", (unsigned long long) btrfs_qgroup_info_generation(l, qg_info), (long long) btrfs_qgroup_info_referenced(l, qg_info), (long long) btrfs_qgroup_info_referenced_compressed(l, qg_info), (long long) btrfs_qgroup_info_exclusive(l, qg_info), (long long) btrfs_qgroup_info_exclusive_compressed(l, qg_info)); break; case BTRFS_QGROUP_LIMIT_KEY: qg_limit = btrfs_item_ptr(l, i, struct btrfs_qgroup_limit_item); printf("\t\tflags %llx\n" "\t\tmax referenced %lld max exclusive %lld\n" "\t\trsv referenced %lld rsv exclusive %lld\n", (unsigned long long) btrfs_qgroup_limit_flags(l, qg_limit), (long long) btrfs_qgroup_limit_max_referenced(l, qg_limit), (long long) btrfs_qgroup_limit_max_exclusive(l, qg_limit), (long long) btrfs_qgroup_limit_rsv_referenced(l, qg_limit), (long long) btrfs_qgroup_limit_rsv_exclusive(l, qg_limit)); break; case BTRFS_STRING_ITEM_KEY: /* dirty, but it's simple */ str = l->data + btrfs_item_ptr_offset(l, i); printf("\t\titem data %.*s\n", btrfs_item_size(l, item), str); break; case BTRFS_DEV_STATS_KEY: printf("\t\tdevice stats\n"); break; }; fflush(stdout); } }
static int set_file_xattrs(struct btrfs_root *root, u64 inode, int fd, const char *file_name) { struct btrfs_key key; struct btrfs_path *path; struct extent_buffer *leaf; struct btrfs_dir_item *di; u32 name_len = 0; u32 data_len = 0; u32 len = 0; u32 cur, total_len; char *name = NULL; char *data = NULL; int ret = 0; key.objectid = inode; key.type = BTRFS_XATTR_ITEM_KEY; key.offset = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto out; leaf = path->nodes[0]; while (1) { if (path->slots[0] >= btrfs_header_nritems(leaf)) { do { ret = next_leaf(root, path); if (ret < 0) { fprintf(stderr, "Error searching for extended attributes: %d\n", ret); goto out; } else if (ret) { /* No more leaves to search */ ret = 0; goto out; } leaf = path->nodes[0]; } while (!leaf); continue; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.type != BTRFS_XATTR_ITEM_KEY || key.objectid != inode) break; cur = 0; total_len = btrfs_item_size_nr(leaf, path->slots[0]); di = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); while (cur < total_len) { len = btrfs_dir_name_len(leaf, di); if (len > name_len) { free(name); name = (char *) malloc(len + 1); if (!name) { ret = -ENOMEM; goto out; } } read_extent_buffer(leaf, name, (unsigned long)(di + 1), len); name[len] = '\0'; name_len = len; len = btrfs_dir_data_len(leaf, di); if (len > data_len) { free(data); data = (char *) malloc(len); if (!data) { ret = -ENOMEM; goto out; } } read_extent_buffer(leaf, data, (unsigned long)(di + 1) + name_len, len); data_len = len; if (fsetxattr(fd, name, data, data_len, 0)) { int err = errno; fprintf(stderr, "Error setting extended attribute %s on file %s: %s\n", name, file_name, strerror(err)); } len = sizeof(*di) + name_len + data_len; cur += len; di = (struct btrfs_dir_item *)((char *)di + len); } path->slots[0]++; } ret = 0; out: btrfs_free_path(path); free(name); free(data); return ret; }
/* * walks the btree of allocated inodes and find a hole. */ int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 dirid, u64 *objectid) { struct btrfs_path *path; struct btrfs_key key; int ret; int slot = 0; u64 last_ino = 0; int start_found; struct extent_buffer *l; struct btrfs_key search_key; u64 search_start = dirid; mutex_lock(&root->objectid_mutex); if (root->last_inode_alloc >= BTRFS_FIRST_FREE_OBJECTID && root->last_inode_alloc < BTRFS_LAST_FREE_OBJECTID) { *objectid = ++root->last_inode_alloc; mutex_unlock(&root->objectid_mutex); return 0; } path = btrfs_alloc_path(); BUG_ON(!path); search_start = max(search_start, BTRFS_FIRST_FREE_OBJECTID); search_key.objectid = search_start; search_key.type = 0; search_key.offset = 0; start_found = 0; ret = btrfs_search_slot(trans, root, &search_key, path, 0, 0); if (ret < 0) goto error; while (1) { l = path->nodes[0]; slot = path->slots[0]; if (slot >= btrfs_header_nritems(l)) { ret = btrfs_next_leaf(root, path); if (ret == 0) continue; if (ret < 0) goto error; if (!start_found) { *objectid = search_start; start_found = 1; goto found; } *objectid = last_ino > search_start ? last_ino : search_start; goto found; } btrfs_item_key_to_cpu(l, &key, slot); if (key.objectid >= search_start) { if (start_found) { if (last_ino < search_start) last_ino = search_start; if (key.objectid > last_ino) { *objectid = last_ino; goto found; } } else if (key.objectid > search_start) { *objectid = search_start; goto found; } } if (key.objectid >= BTRFS_LAST_FREE_OBJECTID) break; start_found = 1; last_ino = key.objectid + 1; path->slots[0]++; } BUG_ON(1); found: btrfs_release_path(root, path); btrfs_free_path(path); BUG_ON(*objectid < search_start); mutex_unlock(&root->objectid_mutex); return 0; error: btrfs_release_path(root, path); btrfs_free_path(path); mutex_unlock(&root->objectid_mutex); return ret; }
/* * Get the first file extent that covers (part of) the given range * Unlike kernel using extent_map to handle hole even no-hole is enabled, * progs don't have such infrastructure, so caller should do extra care * for no-hole. * * return 0 for found, and path points to the file extent. * return >0 for not found, and path points to the insert position. * return <0 for error. */ int btrfs_get_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 ino, u64 offset, u64 len, int ins_len) { struct btrfs_key key; struct btrfs_key found_key; struct btrfs_file_extent_item *fi_item; u64 end = 0; int ret = 0; int not_found = 1; key.objectid = ino; key.type = BTRFS_EXTENT_DATA_KEY; key.offset = offset; ret = btrfs_search_slot(trans, root, &key, path, ins_len, ins_len ? 1 : 0); if (ret <= 0) goto out; if (ret > 0) { /* Check preivous file extent */ ret = btrfs_previous_item(root, path, ino, BTRFS_EXTENT_DATA_KEY); if (ret < 0) goto out; if (ret > 0) goto check_next; } btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); if (found_key.objectid != ino || found_key.type != BTRFS_EXTENT_DATA_KEY) goto check_next; fi_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_file_extent_item); end = found_key.offset + btrfs_file_extent_ram_bytes(path->nodes[0], fi_item); /* * existing file extent * |--------| |----| * |-------| * offset + len * OR * |---------------| * |-------| */ if (end > offset) { not_found = 0; goto out; } check_next: ret = btrfs_next_item(root, path); if (ret) goto out; btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); if (found_key.objectid != ino || found_key.type != BTRFS_EXTENT_DATA_KEY) { ret = 1; goto out; } if (found_key.offset < offset + len) /* * existing file extent * |---| |------| * |-------| * offset + len */ not_found = 0; else /* * existing file extent * |----| |----| * |----| * offset + len */ not_found = 1; /* * To keep the search hehavior consistent with search_slot(), * we need to go back to the prev leaf's nritem slot if * we are at the first slot of the leaf. */ if (path->slots[0] == 0) { ret = btrfs_prev_leaf(root, path); /* Not possible */ if (ret) goto out; path->slots[0] = btrfs_header_nritems(path->nodes[0]); } out: if (ret == 0) ret = not_found; return ret; }
/* * walks the btree of allocated inodes and find a hole. */ int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 dirid, u64 *objectid) { struct btrfs_path *path; struct btrfs_key key; int ret; int slot = 0; u64 last_ino = 0; int start_found; struct extent_buffer *l; struct btrfs_key search_key; u64 search_start = dirid; path = btrfs_alloc_path(); BUG_ON(!path); search_start = root->last_inode_alloc; search_start = max((unsigned long long)search_start, BTRFS_FIRST_FREE_OBJECTID); search_key.objectid = search_start; search_key.offset = 0; btrfs_init_path(path); start_found = 0; ret = btrfs_search_slot(trans, root, &search_key, path, 0, 0); if (ret < 0) goto error; if (path->slots[0] > 0) path->slots[0]--; while (1) { l = path->nodes[0]; slot = path->slots[0]; if (slot >= btrfs_header_nritems(l)) { ret = btrfs_next_leaf(root, path); if (ret == 0) continue; if (ret < 0) goto error; if (!start_found) { *objectid = search_start; start_found = 1; goto found; } *objectid = last_ino > search_start ? last_ino : search_start; goto found; } btrfs_item_key_to_cpu(l, &key, slot); if (key.objectid >= search_start) { if (start_found) { if (last_ino < search_start) last_ino = search_start; if (key.objectid > last_ino) { *objectid = last_ino; goto found; } } } start_found = 1; last_ino = key.objectid + 1; path->slots[0]++; } // FIXME -ENOSPC found: root->last_inode_alloc = *objectid; btrfs_release_path(root, path); btrfs_free_path(path); BUG_ON(*objectid < search_start); return 0; error: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size) { struct btrfs_key key, found_key; struct inode *inode = dentry->d_inode; struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_path *path; struct extent_buffer *leaf; struct btrfs_dir_item *di; int ret = 0, slot; size_t total_size = 0, size_left = size; unsigned long name_ptr; size_t name_len; /* * ok we want all objects associated with this id. * NOTE: we set key.offset = 0; because we want to start with the * first xattr that we find and walk forward */ key.objectid = btrfs_ino(inode); btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); key.offset = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; path->reada = 2; /* search for our xattrs */ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto err; while (1) { leaf = path->nodes[0]; slot = path->slots[0]; /* this is where we start walking through the path */ if (slot >= btrfs_header_nritems(leaf)) { /* * if we've reached the last slot in this leaf we need * to go to the next leaf and reset everything */ ret = btrfs_next_leaf(root, path); if (ret < 0) goto err; else if (ret > 0) break; continue; } btrfs_item_key_to_cpu(leaf, &found_key, slot); /* check to make sure this item is what we want */ if (found_key.objectid != key.objectid) break; if (btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY) break; di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); if (verify_dir_item(root, leaf, di)) continue; name_len = btrfs_dir_name_len(leaf, di); total_size += name_len + 1; /* we are just looking for how big our buffer needs to be */ if (!size) goto next; if (!buffer || (name_len + 1) > size_left) { ret = -ERANGE; goto err; } name_ptr = (unsigned long)(di + 1); read_extent_buffer(leaf, buffer, name_ptr, name_len); buffer[name_len] = '\0'; size_left -= name_len + 1; buffer += name_len + 1; next: path->slots[0]++; } ret = total_size; err: btrfs_free_path(path); return ret; }
/* * at mount time we want to find all the old transaction snapshots that were in * the process of being deleted if we crashed. This is any root item with an * offset lower than the latest root. They need to be queued for deletion to * finish what was happening when we crashed. */ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid) { struct btrfs_root *dead_root; struct btrfs_item *item; struct btrfs_root_item *ri; struct btrfs_key key; struct btrfs_key found_key; struct btrfs_path *path; int ret; u32 nritems; struct extent_buffer *leaf; int slot; key.objectid = objectid; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); key.offset = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; again: ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto err; while (1) { leaf = path->nodes[0]; nritems = btrfs_header_nritems(leaf); slot = path->slots[0]; if (slot >= nritems) { ret = btrfs_next_leaf(root, path); if (ret) break; leaf = path->nodes[0]; nritems = btrfs_header_nritems(leaf); slot = path->slots[0]; } item = btrfs_item_nr(leaf, slot); btrfs_item_key_to_cpu(leaf, &key, slot); if (btrfs_key_type(&key) != BTRFS_ROOT_ITEM_KEY) goto next; if (key.objectid < objectid) goto next; if (key.objectid > objectid) break; ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item); if (btrfs_disk_root_refs(leaf, ri) != 0) goto next; memcpy(&found_key, &key, sizeof(key)); key.offset++; btrfs_release_path(root, path); dead_root = btrfs_read_fs_root_no_radix(root->fs_info->tree_root, &found_key); if (IS_ERR(dead_root)) { ret = PTR_ERR(dead_root); goto err; } ret = btrfs_add_dead_root(dead_root); if (ret) goto err; goto again; next: slot++; path->slots[0]++; } ret = 0; err: btrfs_free_path(path); return ret; }
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) { int i; u32 type; u32 nr = btrfs_header_nritems(l); struct btrfs_item *item; struct btrfs_root_item *ri; struct btrfs_dir_item *di; struct btrfs_inode_item *ii; struct btrfs_block_group_item *bi; struct btrfs_file_extent_item *fi; struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; struct btrfs_dev_extent *dev_extent; struct btrfs_key key; struct btrfs_key found_key; printk(KERN_INFO "leaf %llu total ptrs %d free space %d\n", (unsigned long long)btrfs_header_bytenr(l), nr, btrfs_leaf_free_space(root, l)); for (i = 0 ; i < nr ; i++) { item = btrfs_item_nr(l, i); btrfs_item_key_to_cpu(l, &key, i); type = btrfs_key_type(&key); printk(KERN_INFO "\titem %d key (%llu %x %llu) itemoff %d " "itemsize %d\n", i, (unsigned long long)key.objectid, type, (unsigned long long)key.offset, btrfs_item_offset(l, item), btrfs_item_size(l, item)); switch (type) { case BTRFS_INODE_ITEM_KEY: ii = btrfs_item_ptr(l, i, struct btrfs_inode_item); printk(KERN_INFO "\t\tinode generation %llu size %llu " "mode %o\n", (unsigned long long) btrfs_inode_generation(l, ii), (unsigned long long)btrfs_inode_size(l, ii), btrfs_inode_mode(l, ii)); break; case BTRFS_DIR_ITEM_KEY: di = btrfs_item_ptr(l, i, struct btrfs_dir_item); btrfs_dir_item_key_to_cpu(l, di, &found_key); printk(KERN_INFO "\t\tdir oid %llu type %u\n", (unsigned long long)found_key.objectid, btrfs_dir_type(l, di)); break; case BTRFS_ROOT_ITEM_KEY: ri = btrfs_item_ptr(l, i, struct btrfs_root_item); printk(KERN_INFO "\t\troot data bytenr %llu refs %u\n", (unsigned long long) btrfs_disk_root_bytenr(l, ri), btrfs_disk_root_refs(l, ri)); break; case BTRFS_EXTENT_ITEM_KEY: print_extent_item(l, i); break; case BTRFS_TREE_BLOCK_REF_KEY: printk(KERN_INFO "\t\ttree block backref\n"); break; case BTRFS_SHARED_BLOCK_REF_KEY: printk(KERN_INFO "\t\tshared block backref\n"); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = btrfs_item_ptr(l, i, struct btrfs_extent_data_ref); print_extent_data_ref(l, dref); break; case BTRFS_SHARED_DATA_REF_KEY: sref = btrfs_item_ptr(l, i, struct btrfs_shared_data_ref); printk(KERN_INFO "\t\tshared data backref count %u\n", btrfs_shared_data_ref_count(l, sref)); break; case BTRFS_EXTENT_DATA_KEY: fi = btrfs_item_ptr(l, i, struct btrfs_file_extent_item); if (btrfs_file_extent_type(l, fi) == BTRFS_FILE_EXTENT_INLINE) { printk(KERN_INFO "\t\tinline extent data " "size %u\n", btrfs_file_extent_inline_len(l, fi)); break; } printk(KERN_INFO "\t\textent data disk bytenr %llu " "nr %llu\n", (unsigned long long) btrfs_file_extent_disk_bytenr(l, fi), (unsigned long long) btrfs_file_extent_disk_num_bytes(l, fi)); printk(KERN_INFO "\t\textent data offset %llu " "nr %llu ram %llu\n", (unsigned long long) btrfs_file_extent_offset(l, fi), (unsigned long long) btrfs_file_extent_num_bytes(l, fi), (unsigned long long) btrfs_file_extent_ram_bytes(l, fi)); break; case BTRFS_EXTENT_REF_V0_KEY: #ifdef BTRFS_COMPAT_EXTENT_TREE_V0 print_extent_ref_v0(l, i); #else BUG(); #endif break; case BTRFS_BLOCK_GROUP_ITEM_KEY: bi = btrfs_item_ptr(l, i, struct btrfs_block_group_item); printk(KERN_INFO "\t\tblock group used %llu\n", (unsigned long long) btrfs_disk_block_group_used(l, bi)); break; case BTRFS_CHUNK_ITEM_KEY: print_chunk(l, btrfs_item_ptr(l, i, struct btrfs_chunk)); break; case BTRFS_DEV_ITEM_KEY: print_dev_item(l, btrfs_item_ptr(l, i, struct btrfs_dev_item)); break; case BTRFS_DEV_EXTENT_KEY: dev_extent = btrfs_item_ptr(l, i, struct btrfs_dev_extent); printk(KERN_INFO "\t\tdev extent chunk_tree %llu\n" "\t\tchunk objectid %llu chunk offset %llu " "length %llu\n", (unsigned long long) btrfs_dev_extent_chunk_tree(l, dev_extent), (unsigned long long) btrfs_dev_extent_chunk_objectid(l, dev_extent), (unsigned long long) btrfs_dev_extent_chunk_offset(l, dev_extent), (unsigned long long) btrfs_dev_extent_length(l, dev_extent)); }; } }
static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, struct ulist *parents, int level, struct btrfs_key *key_for_search, u64 time_seq, u64 wanted_disk_byte, const u64 *extent_item_pos) { int ret = 0; int slot; struct extent_buffer *eb; struct btrfs_key key; struct btrfs_file_extent_item *fi; struct extent_inode_elem *eie = NULL, *old = NULL; u64 disk_byte; if (level != 0) { eb = path->nodes[level]; ret = ulist_add(parents, eb->start, 0, GFP_NOFS); if (ret < 0) return ret; return 0; } /* * We normally enter this function with the path already pointing to * the first item to check. But sometimes, we may enter it with * slot==nritems. In that case, go to the next leaf before we continue. */ if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) ret = btrfs_next_old_leaf(root, path, time_seq); while (!ret) { eb = path->nodes[0]; slot = path->slots[0]; btrfs_item_key_to_cpu(eb, &key, slot); if (key.objectid != key_for_search->objectid || key.type != BTRFS_EXTENT_DATA_KEY) break; fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); if (disk_byte == wanted_disk_byte) { eie = NULL; old = NULL; if (extent_item_pos) { ret = check_extent_in_eb(&key, eb, fi, *extent_item_pos, &eie); if (ret < 0) break; } if (ret > 0) goto next; ret = ulist_add_merge(parents, eb->start, (uintptr_t)eie, (u64 *)&old, GFP_NOFS); if (ret < 0) break; if (!ret && extent_item_pos) { while (old->next) old = old->next; old->next = eie; } } next: ret = btrfs_next_old_item(root, path, time_seq); } if (ret > 0) ret = 0; return ret; }
int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, u64 start_off, struct btrfs_path *path, struct btrfs_inode_extref **ret_extref, u64 *found_off) { int ret, slot; struct btrfs_key key; struct btrfs_key found_key; struct btrfs_inode_extref *extref; struct extent_buffer *leaf; unsigned long ptr; key.objectid = inode_objectid; btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY); key.offset = start_off; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) return ret; while (1) { leaf = path->nodes[0]; slot = path->slots[0]; if (slot >= btrfs_header_nritems(leaf)) { /* * If the item at offset is not found, * btrfs_search_slot will point us to the slot * where it should be inserted. In our case * that will be the slot directly before the * next INODE_REF_KEY_V2 item. In the case * that we're pointing to the last slot in a * leaf, we must move one leaf over. */ ret = btrfs_next_leaf(root, path); if (ret) { if (ret >= 1) ret = -ENOENT; break; } continue; } btrfs_item_key_to_cpu(leaf, &found_key, slot); /* * Check that we're still looking at an extended ref key for * this particular objectid. If we have different * objectid or type then there are no more to be found * in the tree and we can exit. */ ret = -ENOENT; if (found_key.objectid != inode_objectid) break; if (btrfs_key_type(&found_key) != BTRFS_INODE_EXTREF_KEY) break; ret = 0; ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); extref = (struct btrfs_inode_extref *)ptr; *ret_extref = extref; if (found_off) *found_off = found_key.offset; break; } return ret; }
int btrfs_find_orphan_roots(struct btrfs_root *tree_root) { struct extent_buffer *leaf; struct btrfs_path *path; struct btrfs_key key; struct btrfs_key root_key; struct btrfs_root *root; int err = 0; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; key.objectid = BTRFS_ORPHAN_OBJECTID; key.type = BTRFS_ORPHAN_ITEM_KEY; key.offset = 0; root_key.type = BTRFS_ROOT_ITEM_KEY; root_key.offset = (u64)-1; while (1) { ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); if (ret < 0) { err = ret; break; } leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(tree_root, path); if (ret < 0) err = ret; if (ret != 0) break; leaf = path->nodes[0]; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); btrfs_release_path(tree_root, path); if (key.objectid != BTRFS_ORPHAN_OBJECTID || key.type != BTRFS_ORPHAN_ITEM_KEY) break; root_key.objectid = key.offset; key.offset++; root = btrfs_read_fs_root_no_name(tree_root->fs_info, &root_key); if (!IS_ERR(root)) continue; ret = PTR_ERR(root); if (ret != -ENOENT) { err = ret; break; } ret = btrfs_find_dead_roots(tree_root, root_key.objectid); if (ret) { err = ret; break; } } btrfs_free_path(path); return err; }