struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 dir, const char *name, u16 name_len, int mod) { int ret; struct btrfs_key key; int ins_len = mod < 0 ? -1 : 0; int cow = mod != 0; struct btrfs_key found_key; struct extent_buffer *leaf; key.objectid = dir; btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY); key.offset = btrfs_name_hash(name, name_len); ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); if (ret < 0) return ERR_PTR(ret); if (ret > 0) { if (path->slots[0] == 0) return NULL; path->slots[0]--; } leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); if (found_key.objectid != dir || btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY || found_key.offset != key.offset) return NULL; return btrfs_match_dir_item_name(root, path, name, name_len); }
static struct btrfs_csum_item * btrfs_lookup_csum(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 bytenr, int cow) { int ret; struct btrfs_key file_key; struct btrfs_key found_key; struct btrfs_csum_item *item; struct extent_buffer *leaf; u64 csum_offset = 0; u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy); int csums_in_item; file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; file_key.offset = bytenr; btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); if (ret < 0) goto fail; leaf = path->nodes[0]; if (ret > 0) { ret = 1; if (path->slots[0] == 0) goto fail; path->slots[0]--; btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY) goto fail; csum_offset = (bytenr - found_key.offset) / root->sectorsize; csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); csums_in_item /= csum_size; if (csum_offset >= csums_in_item) { ret = -EFBIG; goto fail; } } item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); item = (struct btrfs_csum_item *)((unsigned char *)item + csum_offset * csum_size); return item; fail: if (ret > 0) ret = -ENOENT; return ERR_PTR(ret); }
static struct btrfs_csum_item * btrfs_lookup_csum(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 bytenr, int cow) { int ret; struct btrfs_key file_key; struct btrfs_key found_key; struct btrfs_csum_item *item; struct extent_buffer *leaf; u64 csum_offset = 0; u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy); int csums_in_item; file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; file_key.offset = bytenr; btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); if (ret < 0) goto fail; leaf = path->nodes[0]; if (ret > 0) { ret = 1; if (path->slots[0] == 0) goto fail; path->slots[0]--; btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); if (btrfs_key_type(&found_key) != BTRFS_EXTENT_CSUM_KEY) goto fail; csum_offset = (bytenr - found_key.offset) >> root->fs_info->sb->s_blocksize_bits; csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); csums_in_item /= csum_size; if (csum_offset == csums_in_item) { ret = -EFBIG; goto fail; } else if (csum_offset > csums_in_item) { goto fail; } }
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; }
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)); }; } }
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; }
/* * 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; }
int main(int ac, char **av) { struct btrfs_root *root; struct btrfs_fs_info *info; struct btrfs_path path; struct btrfs_key key; struct btrfs_root_item ri; struct extent_buffer *leaf; struct btrfs_disk_key disk_key; struct btrfs_key found_key; char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; int ret; int slot; int extent_only = 0; int device_only = 0; int uuid_tree_only = 0; int roots_only = 0; int root_backups = 0; u64 block_only = 0; struct btrfs_root *tree_root_scan; u64 tree_id = 0; radix_tree_init(); while(1) { int c; static const struct option long_options[] = { { "help", no_argument, NULL, GETOPT_VAL_HELP}, { NULL, 0, NULL, 0 } }; c = getopt_long(ac, av, "deb:rRut:", long_options, NULL); if (c < 0) break; switch(c) { case 'e': extent_only = 1; break; case 'd': device_only = 1; break; case 'r': roots_only = 1; break; case 'u': uuid_tree_only = 1; break; case 'R': roots_only = 1; root_backups = 1; break; case 'b': block_only = arg_strtou64(optarg); break; case 't': tree_id = arg_strtou64(optarg); break; case GETOPT_VAL_HELP: default: print_usage(c != GETOPT_VAL_HELP); } } set_argv0(av); ac = ac - optind; if (check_argc_exact(ac, 1)) print_usage(1); ret = check_arg_type(av[optind]); if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) { fprintf(stderr, "'%s' is not a block device or regular file\n", av[optind]); exit(1); } info = open_ctree_fs_info(av[optind], 0, 0, OPEN_CTREE_PARTIAL); if (!info) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); } root = info->fs_root; if (!root) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); } if (block_only) { leaf = read_tree_block(root, block_only, root->leafsize, 0); if (extent_buffer_uptodate(leaf) && btrfs_header_level(leaf) != 0) { free_extent_buffer(leaf); leaf = NULL; } if (!leaf) { leaf = read_tree_block(root, block_only, root->nodesize, 0); } if (!extent_buffer_uptodate(leaf)) { fprintf(stderr, "failed to read %llu\n", (unsigned long long)block_only); goto close_root; } btrfs_print_tree(root, leaf, 0); free_extent_buffer(leaf); goto close_root; } if (!(extent_only || uuid_tree_only || tree_id)) { if (roots_only) { printf("root tree: %llu level %d\n", (unsigned long long)info->tree_root->node->start, btrfs_header_level(info->tree_root->node)); printf("chunk tree: %llu level %d\n", (unsigned long long)info->chunk_root->node->start, btrfs_header_level(info->chunk_root->node)); } else { if (info->tree_root->node) { printf("root tree\n"); btrfs_print_tree(info->tree_root, info->tree_root->node, 1); } if (info->chunk_root->node) { printf("chunk tree\n"); btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1); } } } tree_root_scan = info->tree_root; btrfs_init_path(&path); again: if (!extent_buffer_uptodate(tree_root_scan->node)) goto no_node; /* * Tree's that are not pointed by the tree of tree roots */ if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) { if (!info->tree_root->node) { error("cannot print root tree, invalid pointer"); goto no_node; } printf("root tree\n"); btrfs_print_tree(info->tree_root, info->tree_root->node, 1); goto no_node; } if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) { if (!info->chunk_root->node) { error("cannot print chunk tree, invalid pointer"); goto no_node; } printf("chunk tree\n"); btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1); goto no_node; } key.offset = 0; key.objectid = 0; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0); BUG_ON(ret < 0); while(1) { leaf = path.nodes[0]; slot = path.slots[0]; if (slot >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(tree_root_scan, &path); if (ret != 0) break; leaf = path.nodes[0]; slot = path.slots[0]; } btrfs_item_key(leaf, &disk_key, path.slots[0]); btrfs_disk_key_to_cpu(&found_key, &disk_key); if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { unsigned long offset; struct extent_buffer *buf; int skip = extent_only | device_only | uuid_tree_only; offset = btrfs_item_ptr_offset(leaf, slot); read_extent_buffer(leaf, &ri, offset, sizeof(ri)); buf = read_tree_block(tree_root_scan, btrfs_root_bytenr(&ri), btrfs_level_size(tree_root_scan, btrfs_root_level(&ri)), 0); if (!extent_buffer_uptodate(buf)) goto next; if (tree_id && found_key.objectid != tree_id) { free_extent_buffer(buf); goto next; } switch(found_key.objectid) { case BTRFS_ROOT_TREE_OBJECTID: if (!skip) printf("root"); break; case BTRFS_EXTENT_TREE_OBJECTID: if (!device_only && !uuid_tree_only) skip = 0; if (!skip) printf("extent"); break; case BTRFS_CHUNK_TREE_OBJECTID: if (!skip) { printf("chunk"); } break; case BTRFS_DEV_TREE_OBJECTID: if (!uuid_tree_only) skip = 0; if (!skip) printf("device"); break; case BTRFS_FS_TREE_OBJECTID: if (!skip) { printf("fs"); } break; case BTRFS_ROOT_TREE_DIR_OBJECTID: skip = 0; printf("directory"); break; case BTRFS_CSUM_TREE_OBJECTID: if (!skip) { printf("checksum"); } break; case BTRFS_ORPHAN_OBJECTID: if (!skip) { printf("orphan"); } break; case BTRFS_TREE_LOG_OBJECTID: if (!skip) { printf("log"); } break; case BTRFS_TREE_LOG_FIXUP_OBJECTID: if (!skip) { printf("log fixup"); } break; case BTRFS_TREE_RELOC_OBJECTID: if (!skip) { printf("reloc"); } break; case BTRFS_DATA_RELOC_TREE_OBJECTID: if (!skip) { printf("data reloc"); } break; case BTRFS_EXTENT_CSUM_OBJECTID: if (!skip) { printf("extent checksum"); } break; case BTRFS_QUOTA_TREE_OBJECTID: if (!skip) { printf("quota"); } break; case BTRFS_UUID_TREE_OBJECTID: if (!extent_only && !device_only) skip = 0; if (!skip) printf("uuid"); break; case BTRFS_FREE_SPACE_TREE_OBJECTID: if (!skip) printf("free space"); break; case BTRFS_MULTIPLE_OBJECTIDS: if (!skip) { printf("multiple"); } break; default: if (!skip) { printf("file"); } } if (extent_only && !skip) { print_extents(tree_root_scan, buf); } else if (!skip) { printf(" tree "); btrfs_print_key(&disk_key); if (roots_only) { printf(" %llu level %d\n", (unsigned long long)buf->start, btrfs_header_level(buf)); } else { printf(" \n"); btrfs_print_tree(tree_root_scan, buf, 1); } } free_extent_buffer(buf); } next: path.slots[0]++; } no_node: btrfs_release_path(&path); if (tree_root_scan == info->tree_root && info->log_root_tree) { tree_root_scan = info->log_root_tree; goto again; } if (extent_only || device_only || uuid_tree_only) goto close_root; if (root_backups) print_old_roots(info->super_copy); printf("total bytes %llu\n", (unsigned long long)btrfs_super_total_bytes(info->super_copy)); printf("bytes used %llu\n", (unsigned long long)btrfs_super_bytes_used(info->super_copy)); uuidbuf[BTRFS_UUID_UNPARSED_SIZE - 1] = '\0'; uuid_unparse(info->super_copy->fsid, uuidbuf); printf("uuid %s\n", uuidbuf); printf("%s\n", PACKAGE_STRING); close_root: ret = close_ctree(root); btrfs_close_all_devices(); return ret; }
int main(int ac, char **av) { struct btrfs_root *root; struct btrfs_fs_info *info; struct btrfs_path path; struct btrfs_key key; struct btrfs_root_item ri; struct extent_buffer *leaf; struct btrfs_disk_key disk_key; struct btrfs_key found_key; char uuidbuf[37]; int ret; int slot; int extent_only = 0; int device_only = 0; int roots_only = 0; int root_backups = 0; u64 block_only = 0; struct btrfs_root *tree_root_scan; radix_tree_init(); while(1) { int c; c = getopt(ac, av, "deb:rR"); if (c < 0) break; switch(c) { case 'e': extent_only = 1; break; case 'd': device_only = 1; break; case 'r': roots_only = 1; break; case 'R': roots_only = 1; root_backups = 1; break; case 'b': block_only = atoll(optarg); break; default: print_usage(); } } ac = ac - optind; if (ac != 1) print_usage(); info = open_ctree_fs_info(av[optind], 0, 0, 1); if (!info) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); } root = info->fs_root; if (block_only) { if (!root) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); } leaf = read_tree_block(root, block_only, root->leafsize, 0); if (leaf && btrfs_header_level(leaf) != 0) { free_extent_buffer(leaf); leaf = NULL; } if (!leaf) { leaf = read_tree_block(root, block_only, root->nodesize, 0); } if (!leaf) { fprintf(stderr, "failed to read %llu\n", (unsigned long long)block_only); return 0; } btrfs_print_tree(root, leaf, 0); return 0; } if (!extent_only) { if (roots_only) { printf("root tree: %llu level %d\n", (unsigned long long)info->tree_root->node->start, btrfs_header_level(info->tree_root->node)); printf("chunk tree: %llu level %d\n", (unsigned long long)info->chunk_root->node->start, btrfs_header_level(info->chunk_root->node)); } else { if (info->tree_root->node) { printf("root tree\n"); btrfs_print_tree(info->tree_root, info->tree_root->node, 1); } if (info->chunk_root->node) { printf("chunk tree\n"); btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1); } } } tree_root_scan = info->tree_root; btrfs_init_path(&path); again: if (!extent_buffer_uptodate(tree_root_scan->node)) goto no_node; key.offset = 0; key.objectid = 0; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0); BUG_ON(ret < 0); while(1) { leaf = path.nodes[0]; slot = path.slots[0]; if (slot >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(tree_root_scan, &path); if (ret != 0) break; leaf = path.nodes[0]; slot = path.slots[0]; } btrfs_item_key(leaf, &disk_key, path.slots[0]); btrfs_disk_key_to_cpu(&found_key, &disk_key); if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) { unsigned long offset; struct extent_buffer *buf; int skip = extent_only | device_only; offset = btrfs_item_ptr_offset(leaf, slot); read_extent_buffer(leaf, &ri, offset, sizeof(ri)); buf = read_tree_block(tree_root_scan, btrfs_root_bytenr(&ri), btrfs_level_size(tree_root_scan, btrfs_root_level(&ri)), 0); if (!extent_buffer_uptodate(buf)) goto next; switch(found_key.objectid) { case BTRFS_ROOT_TREE_OBJECTID: if (!skip) printf("root"); break; case BTRFS_EXTENT_TREE_OBJECTID: if (!device_only) skip = 0; if (!extent_only && !device_only) printf("extent"); break; case BTRFS_CHUNK_TREE_OBJECTID: if (!skip) { printf("chunk"); } break; case BTRFS_DEV_TREE_OBJECTID: skip = 0; printf("device"); break; case BTRFS_FS_TREE_OBJECTID: if (!skip) { printf("fs"); } break; case BTRFS_ROOT_TREE_DIR_OBJECTID: skip = 0; printf("directory"); break; case BTRFS_CSUM_TREE_OBJECTID: if (!skip) { printf("checksum"); } break; case BTRFS_ORPHAN_OBJECTID: if (!skip) { printf("orphan"); } break; case BTRFS_TREE_LOG_OBJECTID: if (!skip) { printf("log"); } break; case BTRFS_TREE_LOG_FIXUP_OBJECTID: if (!skip) { printf("log fixup"); } break; case BTRFS_TREE_RELOC_OBJECTID: if (!skip) { printf("reloc"); } break; case BTRFS_DATA_RELOC_TREE_OBJECTID: if (!skip) { printf("data reloc"); } break; case BTRFS_EXTENT_CSUM_OBJECTID: if (!skip) { printf("extent checksum"); } break; case BTRFS_QUOTA_TREE_OBJECTID: if (!skip) { printf("quota"); } break; case BTRFS_MULTIPLE_OBJECTIDS: if (!skip) { printf("multiple"); } break; default: if (!skip) { printf("file"); } } if (extent_only && !skip) { print_extents(tree_root_scan, buf); } else if (!skip) { printf(" tree "); btrfs_print_key(&disk_key); if (roots_only) { printf(" %llu level %d\n", (unsigned long long)buf->start, btrfs_header_level(buf)); } else { printf(" \n"); btrfs_print_tree(tree_root_scan, buf, 1); } } } next: path.slots[0]++; } no_node: btrfs_release_path(root, &path); if (tree_root_scan == info->tree_root && info->log_root_tree) { tree_root_scan = info->log_root_tree; goto again; } if (extent_only || device_only) return 0; if (root_backups) print_old_roots(&info->super_copy); printf("total bytes %llu\n", (unsigned long long)btrfs_super_total_bytes(&info->super_copy)); printf("bytes used %llu\n", (unsigned long long)btrfs_super_bytes_used(&info->super_copy)); uuidbuf[36] = '\0'; uuid_unparse(info->super_copy.fsid, uuidbuf); printf("uuid %s\n", uuidbuf); printf("%s\n", BTRFS_BUILD_VERSION); return 0; }
/* * 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. * * inline_limit is used to tell this code which offsets in the file to keep * if they contain inline extents. */ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct inode *inode, u64 start, u64 end, u64 inline_limit, u64 *hint_byte) { u64 extent_end = 0; u64 locked_end = end; u64 search_start = start; u64 leaf_start; u64 ram_bytes = 0; u64 orig_parent = 0; u64 disk_bytenr = 0; u8 compression; u8 encryption; u16 other_encoding = 0; u64 root_gen; u64 root_owner; struct extent_buffer *leaf; struct btrfs_file_extent_item *extent; struct btrfs_path *path; struct btrfs_key key; struct btrfs_file_extent_item old; int keep; int slot; int bookend; int found_type = 0; int found_extent; int found_inline; int recow; int ret; inline_limit = 0; btrfs_drop_extent_cache(inode, start, end - 1, 0); path = btrfs_alloc_path(); if (!path) return -ENOMEM; while (1) { recow = 0; btrfs_release_path(root, path); ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino, search_start, -1); if (ret < 0) goto out; if (ret > 0) { if (path->slots[0] == 0) { ret = 0; goto out; } path->slots[0]--; } next_slot: keep = 0; bookend = 0; found_extent = 0; found_inline = 0; leaf_start = 0; root_gen = 0; root_owner = 0; compression = 0; encryption = 0; extent = NULL; leaf = path->nodes[0]; slot = path->slots[0]; ret = 0; btrfs_item_key_to_cpu(leaf, &key, slot); if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY && key.offset >= end) { goto out; } if (btrfs_key_type(&key) > BTRFS_EXTENT_DATA_KEY || key.objectid != inode->i_ino) { goto out; } if (recow) { search_start = max(key.offset, start); continue; } if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) { extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); found_type = btrfs_file_extent_type(leaf, extent); compression = btrfs_file_extent_compression(leaf, extent); encryption = btrfs_file_extent_encryption(leaf, extent); other_encoding = btrfs_file_extent_other_encoding(leaf, extent); if (found_type == BTRFS_FILE_EXTENT_REG || found_type == BTRFS_FILE_EXTENT_PREALLOC) { extent_end = btrfs_file_extent_disk_bytenr(leaf, extent); if (extent_end) *hint_byte = extent_end; extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, extent); ram_bytes = btrfs_file_extent_ram_bytes(leaf, extent); found_extent = 1; } else if (found_type == BTRFS_FILE_EXTENT_INLINE) { found_inline = 1; extent_end = key.offset + btrfs_file_extent_inline_len(leaf, extent); } } else {
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; /* */ 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; /* */ 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]; /* */ if (slot >= btrfs_header_nritems(leaf)) { /* */ 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); /* */ 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; /* */ 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; }
/* * walks the btree of allocated extents and find a hole of a given size. * The key ins is changed to record the hole: * ins->objectid == block start * ins->flags = BTRFS_EXTENT_ITEM_KEY * ins->offset == number of blocks * Any available blocks before search_start are skipped. */ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *orig_root, u64 num_blocks, u64 search_start, u64 search_end, struct btrfs_key *ins) { struct btrfs_path path; struct btrfs_key key; int ret; u64 hole_size = 0; int slot = 0; u64 last_block = 0; u64 test_block; int start_found; struct btrfs_leaf *l; struct btrfs_root * root = orig_root->fs_info->extent_root; unsigned int total_needed = num_blocks; total_needed += (btrfs_header_level(&root->node->node.header) + 1) * 3; if (root->fs_info->last_insert.objectid > search_start) search_start = root->fs_info->last_insert.objectid; ins->flags = 0; btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); check_failed: btrfs_init_path(&path); ins->objectid = search_start; ins->offset = 0; start_found = 0; ret = btrfs_search_slot(trans, root, ins, &path, 0, 0); if (ret < 0) goto error; if (path.slots[0] > 0) path.slots[0]--; while (1) { l = &path.nodes[0]->leaf; slot = path.slots[0]; if (slot >= btrfs_header_nritems(&l->header)) { ret = btrfs_next_leaf(root, &path); if (ret == 0) continue; if (ret < 0) goto error; if (!start_found) { ins->objectid = search_start; ins->offset = (u64)-1 - search_start; start_found = 1; goto check_pending; } ins->objectid = last_block > search_start ? last_block : search_start; ins->offset = (u64)-1 - ins->objectid; goto check_pending; } btrfs_disk_key_to_cpu(&key, &l->items[slot].key); if (btrfs_key_type(&key) != BTRFS_EXTENT_ITEM_KEY) goto next; if (key.objectid >= search_start) { if (start_found) { if (last_block < search_start) last_block = search_start; hole_size = key.objectid - last_block; if (hole_size > total_needed) { ins->objectid = last_block; ins->offset = hole_size; goto check_pending; } } } start_found = 1; last_block = key.objectid + key.offset; next: path.slots[0]++; } // FIXME -ENOSPC check_pending: /* we have to make sure we didn't find an extent that has already * been allocated by the map tree or the original allocation */ btrfs_release_path(root, &path); BUG_ON(ins->objectid < search_start); for (test_block = ins->objectid; test_block < ins->objectid + total_needed; test_block++) { if (radix_tree_lookup(&root->fs_info->pinned_radix, test_block)) { search_start = test_block + 1; goto check_failed; } } BUG_ON(root->fs_info->current_insert.offset); root->fs_info->current_insert.offset = total_needed - num_blocks; root->fs_info->current_insert.objectid = ins->objectid + num_blocks; root->fs_info->current_insert.flags = 0; root->fs_info->last_insert.objectid = ins->objectid; ins->offset = num_blocks; return 0; error: btrfs_release_path(root, &path); return ret; }