int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, u64 offset, char *buffer, size_t size) { struct btrfs_key key; struct btrfs_path *path; struct extent_buffer *leaf; unsigned long ptr; struct btrfs_file_extent_item *ei; u32 datasize; int err = 0; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; key.objectid = objectid; key.offset = offset; btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); datasize = btrfs_file_extent_calc_inline_size(size); ret = btrfs_insert_empty_item(trans, root, path, &key, datasize); if (ret) { err = ret; goto fail; } leaf = path->nodes[0]; ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); btrfs_set_file_extent_generation(leaf, ei, trans->transid); btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE); btrfs_set_file_extent_ram_bytes(leaf, ei, size); btrfs_set_file_extent_compression(leaf, ei, 0); btrfs_set_file_extent_encryption(leaf, ei, 0); btrfs_set_file_extent_other_encoding(leaf, ei, 0); ptr = btrfs_file_extent_inline_start(ei) + offset - key.offset; write_extent_buffer(leaf, buffer, ptr, size); btrfs_mark_buffer_dirty(leaf); fail: btrfs_free_path(path); return err; }
int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, u64 pos, u64 disk_offset, u64 disk_num_bytes, u64 num_bytes, u64 offset, u64 ram_bytes, u8 compression, u8 encryption, u16 other_encoding) { int ret = 0; struct btrfs_file_extent_item *item; struct btrfs_key file_key; struct btrfs_path *path; struct extent_buffer *leaf; path = btrfs_alloc_path(); if (!path) return -ENOMEM; file_key.objectid = objectid; file_key.offset = pos; btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); path->leave_spinning = 1; ret = btrfs_insert_empty_item(trans, root, path, &file_key, sizeof(*item)); if (ret < 0) goto out; BUG_ON(ret); /* Can't happen */ leaf = path->nodes[0]; item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); btrfs_set_file_extent_disk_bytenr(leaf, item, disk_offset); btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); btrfs_set_file_extent_offset(leaf, item, offset); btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); btrfs_set_file_extent_ram_bytes(leaf, item, ram_bytes); btrfs_set_file_extent_generation(leaf, item, trans->transid); btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); btrfs_set_file_extent_compression(leaf, item, compression); btrfs_set_file_extent_encryption(leaf, item, encryption); btrfs_set_file_extent_other_encoding(leaf, item, other_encoding); btrfs_mark_buffer_dirty(leaf); out: btrfs_free_path(path); return ret; }
/* * xattrs work a lot like directories, this inserts an xattr item * into the tree */ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 objectid, const char *name, u16 name_len, const void *data, u16 data_len) { int ret = 0; struct btrfs_dir_item *dir_item; unsigned long name_ptr, data_ptr; struct btrfs_key key, location; struct btrfs_disk_key disk_key; struct extent_buffer *leaf; u32 data_size; if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(root->fs_info)) return -ENOSPC; key.objectid = objectid; key.type = BTRFS_XATTR_ITEM_KEY; key.offset = btrfs_name_hash(name, name_len); data_size = sizeof(*dir_item) + name_len + data_len; dir_item = insert_with_overflow(trans, root, path, &key, data_size, name, name_len); if (IS_ERR(dir_item)) return PTR_ERR(dir_item); memset(&location, 0, sizeof(location)); leaf = path->nodes[0]; btrfs_cpu_key_to_disk(&disk_key, &location); btrfs_set_dir_item_key(leaf, dir_item, &disk_key); btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR); btrfs_set_dir_name_len(leaf, dir_item, name_len); btrfs_set_dir_transid(leaf, dir_item, trans->transid); btrfs_set_dir_data_len(leaf, dir_item, data_len); name_ptr = (unsigned long)(dir_item + 1); data_ptr = (unsigned long)((char *)name_ptr + name_len); write_extent_buffer(leaf, name, name_ptr, name_len); write_extent_buffer(leaf, data, data_ptr, data_len); btrfs_mark_buffer_dirty(path->nodes[0]); return ret; }
static int update_free_space_extent_count(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, int new_extents) { struct btrfs_free_space_info *info; u32 flags; u32 extent_count; int ret = 0; if (new_extents == 0) return 0; info = search_free_space_info(trans, fs_info, block_group, path, 1); if (IS_ERR(info)) { ret = PTR_ERR(info); goto out; } flags = btrfs_free_space_flags(path->nodes[0], info); extent_count = btrfs_free_space_extent_count(path->nodes[0], info); extent_count += new_extents; btrfs_set_free_space_extent_count(path->nodes[0], info, extent_count); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); if (!(flags & BTRFS_FREE_SPACE_USING_BITMAPS) && extent_count > block_group->bitmap_high_thresh) { ret = convert_free_space_to_bitmaps(trans, fs_info, block_group, path); } else if ((flags & BTRFS_FREE_SPACE_USING_BITMAPS) && extent_count < block_group->bitmap_low_thresh) { ret = convert_free_space_to_extents(trans, fs_info, block_group, path); } out: return ret; }
/* * copy the data in 'item' into the btree */ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_root_item *item) { struct btrfs_path *path; struct extent_buffer *l; int ret; int slot; unsigned long ptr; path = btrfs_alloc_path(); if (!path) return -ENOMEM; ret = btrfs_search_slot(trans, root, key, path, 0, 1); if (ret < 0) { btrfs_abort_transaction(trans, root, ret); goto out; } if (ret != 0) { btrfs_print_leaf(root, path->nodes[0]); printk(KERN_CRIT "unable to update root key %llu %u %llu\n", (unsigned long long)key->objectid, key->type, (unsigned long long)key->offset); BUG_ON(1); } l = path->nodes[0]; slot = path->slots[0]; ptr = btrfs_item_ptr_offset(l, slot); write_extent_buffer(l, item, ptr, sizeof(*item)); btrfs_mark_buffer_dirty(path->nodes[0]); out: btrfs_free_path(path); return ret; }
/* * add a btrfs_root_ref item. type is either BTRFS_ROOT_REF_KEY * or BTRFS_ROOT_BACKREF_KEY. * * The dirid, sequence, name and name_len refer to the directory entry * that is referencing the root. * * For a forward ref, the root_id is the id of the tree referencing * the root and ref_id is the id of the subvol or snapshot. * * For a back ref the root_id is the id of the subvol or snapshot and * ref_id is the id of the tree referencing it. */ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, struct btrfs_root *tree_root, u64 root_id, u8 type, u64 ref_id, u64 dirid, u64 sequence, const char *name, int name_len) { struct btrfs_key key; int ret; struct btrfs_path *path; struct btrfs_root_ref *ref; struct extent_buffer *leaf; unsigned long ptr; path = btrfs_alloc_path(); key.objectid = root_id; key.type = type; key.offset = ref_id; ret = btrfs_insert_empty_item(trans, tree_root, path, &key, sizeof(*ref) + name_len); BUG_ON(ret); leaf = path->nodes[0]; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); btrfs_set_root_ref_dirid(leaf, ref, dirid); btrfs_set_root_ref_sequence(leaf, ref, sequence); btrfs_set_root_ref_name_len(leaf, ref, name_len); ptr = (unsigned long)(ref + 1); write_extent_buffer(leaf, name, ptr, name_len); btrfs_mark_buffer_dirty(leaf); btrfs_free_path(path); return ret; }
int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid_cpu) { struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *uuid_root = fs_info->uuid_root; int ret; struct btrfs_path *path = NULL; struct btrfs_key key; struct extent_buffer *eb; int slot; unsigned long offset; __le64 subid_le; ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu); if (ret != -ENOENT) return ret; if (WARN_ON_ONCE(!uuid_root)) { ret = -EINVAL; goto out; } btrfs_uuid_to_key(uuid, type, &key); path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; goto out; } ret = btrfs_insert_empty_item(trans, uuid_root, path, &key, sizeof(subid_le)); if (ret >= 0) { /* Add an item for the type for the first time */ eb = path->nodes[0]; slot = path->slots[0]; offset = btrfs_item_ptr_offset(eb, slot); } else if (ret == -EEXIST) { /* * An item with that type already exists. * Extend the item and store the new subid at the end. */ btrfs_extend_item(path, sizeof(subid_le)); eb = path->nodes[0]; slot = path->slots[0]; offset = btrfs_item_ptr_offset(eb, slot); offset += btrfs_item_size_nr(eb, slot) - sizeof(subid_le); } else { btrfs_warn(fs_info, "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!", ret, (unsigned long long)key.objectid, (unsigned long long)key.offset, type); goto out; } ret = 0; subid_le = cpu_to_le64(subid_cpu); write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le)); btrfs_mark_buffer_dirty(eb); out: btrfs_free_path(path); return ret; }
static noinline int create_subvol(struct btrfs_root *root, struct dentry *dentry, char *name, int namelen) { struct btrfs_trans_handle *trans; struct btrfs_key key; struct btrfs_root_item root_item; struct btrfs_inode_item *inode_item; struct extent_buffer *leaf; struct btrfs_root *new_root; struct inode *dir = dentry->d_parent->d_inode; int ret; int err; u64 objectid; u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; u64 index = 0; ret = btrfs_find_free_objectid(NULL, root->fs_info->tree_root, 0, &objectid); if (ret) return ret; /* * 1 - inode item * 2 - refs * 1 - root item * 2 - dir items */ trans = btrfs_start_transaction(root, 6); if (IS_ERR(trans)) return PTR_ERR(trans); leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0, objectid, NULL, 0, 0, 0); if (IS_ERR(leaf)) { ret = PTR_ERR(leaf); goto fail; } memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header)); btrfs_set_header_bytenr(leaf, leaf->start); btrfs_set_header_generation(leaf, trans->transid); btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(leaf, objectid); write_extent_buffer(leaf, root->fs_info->fsid, (unsigned long)btrfs_header_fsid(leaf), BTRFS_FSID_SIZE); write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, (unsigned long)btrfs_header_chunk_tree_uuid(leaf), BTRFS_UUID_SIZE); btrfs_mark_buffer_dirty(leaf); inode_item = &root_item.inode; memset(inode_item, 0, sizeof(*inode_item)); inode_item->generation = cpu_to_le64(1); inode_item->size = cpu_to_le64(3); inode_item->nlink = cpu_to_le32(1); inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->mode = cpu_to_le32(S_IFDIR | 0755); root_item.flags = 0; root_item.byte_limit = 0; inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); btrfs_set_root_bytenr(&root_item, leaf->start); btrfs_set_root_generation(&root_item, trans->transid); btrfs_set_root_level(&root_item, 0); btrfs_set_root_refs(&root_item, 1); btrfs_set_root_used(&root_item, leaf->len); btrfs_set_root_last_snapshot(&root_item, 0); memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress)); root_item.drop_level = 0; btrfs_tree_unlock(leaf); free_extent_buffer(leaf); leaf = NULL; btrfs_set_root_dirid(&root_item, new_dirid); key.objectid = objectid; key.offset = 0; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, &root_item); if (ret) goto fail; key.offset = (u64)-1; new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); BUG_ON(IS_ERR(new_root)); btrfs_record_root_in_trans(trans, new_root); ret = btrfs_create_subvol_root(trans, new_root, new_dirid, BTRFS_I(dir)->block_group); /* * insert the directory item */ ret = btrfs_set_inode_index(dir, &index); BUG_ON(ret); ret = btrfs_insert_dir_item(trans, root, name, namelen, dir->i_ino, &key, BTRFS_FT_DIR, index); if (ret) goto fail; btrfs_i_size_write(dir, dir->i_size + namelen * 2); ret = btrfs_update_inode(trans, root, dir); BUG_ON(ret); ret = btrfs_add_root_ref(trans, root->fs_info->tree_root, objectid, root->root_key.objectid, dir->i_ino, index, name, namelen); BUG_ON(ret); d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); fail: err = btrfs_commit_transaction(trans, root); if (err && !ret) ret = err; return ret; }
int convert_free_space_to_extents(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { struct btrfs_root *root = fs_info->free_space_root; struct btrfs_free_space_info *info; struct btrfs_key key, found_key; struct extent_buffer *leaf; unsigned long *bitmap; u64 start, end; /* Initialize to silence GCC. */ u64 extent_start = 0; u64 offset; u32 bitmap_size, flags, expected_extent_count; int prev_bit = 0, bit, bitnr; u32 extent_count = 0; int done = 0, nr; int ret; bitmap_size = free_space_bitmap_size(block_group->key.offset, block_group->sectorsize); bitmap = alloc_bitmap(bitmap_size); if (!bitmap) { ret = -ENOMEM; goto out; } start = block_group->key.objectid; end = block_group->key.objectid + block_group->key.offset; key.objectid = end - 1; key.type = (u8)-1; key.offset = (u64)-1; while (!done) { ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (ret) goto out; leaf = path->nodes[0]; nr = 0; path->slots[0]++; while (path->slots[0] > 0) { btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0] - 1); if (found_key.type == BTRFS_FREE_SPACE_INFO_KEY) { ASSERT(found_key.objectid == block_group->key.objectid); ASSERT(found_key.offset == block_group->key.offset); done = 1; break; } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) { unsigned long ptr; char *bitmap_cursor; u32 bitmap_pos, data_size; ASSERT(found_key.objectid >= start); ASSERT(found_key.objectid < end); ASSERT(found_key.objectid + found_key.offset <= end); bitmap_pos = div_u64(found_key.objectid - start, block_group->sectorsize * BITS_PER_BYTE); bitmap_cursor = ((char *)bitmap) + bitmap_pos; data_size = free_space_bitmap_size(found_key.offset, block_group->sectorsize); ptr = btrfs_item_ptr_offset(leaf, path->slots[0] - 1); read_extent_buffer(leaf, bitmap_cursor, ptr, data_size); nr++; path->slots[0]--; } else { ASSERT(0); } } ret = btrfs_del_items(trans, root, path, path->slots[0], nr); if (ret) goto out; btrfs_release_path(path); } info = search_free_space_info(trans, fs_info, block_group, path, 1); if (IS_ERR(info)) { ret = PTR_ERR(info); goto out; } leaf = path->nodes[0]; flags = btrfs_free_space_flags(leaf, info); flags &= ~BTRFS_FREE_SPACE_USING_BITMAPS; btrfs_set_free_space_flags(leaf, info, flags); expected_extent_count = btrfs_free_space_extent_count(leaf, info); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); offset = start; bitnr = 0; while (offset < end) { bit = !!test_bit(bitnr, bitmap); if (prev_bit == 0 && bit == 1) { extent_start = offset; } else if (prev_bit == 1 && bit == 0) { key.objectid = extent_start; key.type = BTRFS_FREE_SPACE_EXTENT_KEY; key.offset = offset - extent_start; ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) goto out; btrfs_release_path(path); extent_count++; } prev_bit = bit; offset += block_group->sectorsize; bitnr++; } if (prev_bit == 1) { key.objectid = extent_start; key.type = BTRFS_FREE_SPACE_EXTENT_KEY; key.offset = end - extent_start; ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) goto out; btrfs_release_path(path); extent_count++; } if (extent_count != expected_extent_count) { btrfs_err(fs_info, "incorrect extent count for %llu; counted %u, expected %u", block_group->key.objectid, extent_count, expected_extent_count); ASSERT(0); ret = -EIO; goto out; } ret = 0; out: kvfree(bitmap); if (ret) btrfs_abort_transaction(trans, ret); return ret; }
/* * called from commit_transaction. Writes changed device replace state to * disk. */ int btrfs_run_dev_replace(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { int ret; struct btrfs_root *dev_root = fs_info->dev_root; struct btrfs_path *path; struct btrfs_key key; struct extent_buffer *eb; struct btrfs_dev_replace_item *ptr; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; btrfs_dev_replace_lock(dev_replace); if (!dev_replace->is_valid || !dev_replace->item_needs_writeback) { btrfs_dev_replace_unlock(dev_replace); return 0; } btrfs_dev_replace_unlock(dev_replace); key.objectid = 0; key.type = BTRFS_DEV_REPLACE_KEY; key.offset = 0; path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; goto out; } ret = btrfs_search_slot(trans, dev_root, &key, path, -1, 1); if (ret < 0) { pr_warn("btrfs: error %d while searching for dev_replace item!\n", ret); goto out; } if (ret == 0 && btrfs_item_size_nr(path->nodes[0], path->slots[0]) < sizeof(*ptr)) { /* * need to delete old one and insert a new one. * Since no attempt is made to recover any old state, if the * dev_replace state is 'running', the data on the target * drive is lost. * It would be possible to recover the state: just make sure * that the beginning of the item is never changed and always * contains all the essential information. Then read this * minimal set of information and use it as a base for the * new state. */ ret = btrfs_del_item(trans, dev_root, path); if (ret != 0) { pr_warn("btrfs: delete too small dev_replace item failed %d!\n", ret); goto out; } ret = 1; } if (ret == 1) { /* need to insert a new item */ btrfs_release_path(path); ret = btrfs_insert_empty_item(trans, dev_root, path, &key, sizeof(*ptr)); if (ret < 0) { pr_warn("btrfs: insert dev_replace item failed %d!\n", ret); goto out; } } eb = path->nodes[0]; ptr = btrfs_item_ptr(eb, path->slots[0], struct btrfs_dev_replace_item); btrfs_dev_replace_lock(dev_replace); if (dev_replace->srcdev) btrfs_set_dev_replace_src_devid(eb, ptr, dev_replace->srcdev->devid); else btrfs_set_dev_replace_src_devid(eb, ptr, (u64)-1); btrfs_set_dev_replace_cont_reading_from_srcdev_mode(eb, ptr, dev_replace->cont_reading_from_srcdev_mode); btrfs_set_dev_replace_replace_state(eb, ptr, dev_replace->replace_state); btrfs_set_dev_replace_time_started(eb, ptr, dev_replace->time_started); btrfs_set_dev_replace_time_stopped(eb, ptr, dev_replace->time_stopped); btrfs_set_dev_replace_num_write_errors(eb, ptr, atomic64_read(&dev_replace->num_write_errors)); btrfs_set_dev_replace_num_uncorrectable_read_errors(eb, ptr, atomic64_read(&dev_replace->num_uncorrectable_read_errors)); dev_replace->cursor_left_last_write_of_item = dev_replace->cursor_left; btrfs_set_dev_replace_cursor_left(eb, ptr, dev_replace->cursor_left_last_write_of_item); btrfs_set_dev_replace_cursor_right(eb, ptr, dev_replace->cursor_right); dev_replace->item_needs_writeback = 0; btrfs_dev_replace_unlock(dev_replace); btrfs_mark_buffer_dirty(eb); out: btrfs_free_path(path); return ret; }
static int ondisk_add(struct btrfs_trans_handle *trans, struct btrfs_dedup_info *dedup_info, struct btrfs_dedup_hash *hash) { struct btrfs_path *path; struct btrfs_root *dedup_root = dedup_info->dedup_root; struct btrfs_key key; struct btrfs_dedup_hash_item *hash_item; u64 bytenr; u32 num_bytes; int hash_len = btrfs_dedup_sizes[dedup_info->hash_type]; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; mutex_lock(&dedup_info->lock); ret = ondisk_search_bytenr(NULL, dedup_info, path, hash->bytenr, 0); if (ret < 0) goto out; if (ret > 0) { ret = 0; goto out; } btrfs_release_path(path); ret = ondisk_search_hash(dedup_info, hash->hash, &bytenr, &num_bytes); if (ret < 0) goto out; /* Same hash found, don't re-add to save dedup tree space */ if (ret > 0) { ret = 0; goto out; } /* Insert hash->bytenr item */ memcpy(&key.objectid, hash->hash + hash_len - 8, 8); key.type = BTRFS_DEDUP_HASH_ITEM_KEY; key.offset = hash->bytenr; ret = btrfs_insert_empty_item(trans, dedup_root, path, &key, sizeof(*hash_item) + hash_len); WARN_ON(ret == -EEXIST); if (ret < 0) goto out; hash_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_dedup_hash_item); btrfs_set_dedup_hash_len(path->nodes[0], hash_item, hash->num_bytes); write_extent_buffer(path->nodes[0], hash->hash, (unsigned long)(hash_item + 1), hash_len); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); /* Then bytenr->hash item */ key.objectid = hash->bytenr; key.type = BTRFS_DEDUP_BYTENR_ITEM_KEY; memcpy(&key.offset, hash->hash + hash_len - 8, 8); ret = btrfs_insert_empty_item(trans, dedup_root, path, &key, hash_len); WARN_ON(ret == -EEXIST); if (ret < 0) goto out; write_extent_buffer(path->nodes[0], hash->hash, btrfs_item_ptr_offset(path->nodes[0], path->slots[0]), hash_len); btrfs_mark_buffer_dirty(path->nodes[0]); out: mutex_unlock(&dedup_info->lock); btrfs_free_path(path); return ret; }
static int record_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 objectid, struct btrfs_inode_item *inode, u64 file_pos, u64 disk_bytenr, u64 num_bytes) { int ret; struct btrfs_fs_info *info = root->fs_info; struct btrfs_root *extent_root = info->extent_root; struct extent_buffer *leaf; struct btrfs_file_extent_item *fi; struct btrfs_key ins_key; struct btrfs_path path; struct btrfs_extent_item *ei; btrfs_init_path(&path); ins_key.objectid = objectid; ins_key.offset = 0; btrfs_set_key_type(&ins_key, BTRFS_EXTENT_DATA_KEY); ret = btrfs_insert_empty_item(trans, root, &path, &ins_key, sizeof(*fi)); if (ret) goto fail; leaf = path.nodes[0]; fi = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_file_extent_item); btrfs_set_file_extent_generation(leaf, fi, trans->transid); btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr); btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); btrfs_set_file_extent_offset(leaf, fi, 0); btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes); btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); btrfs_set_file_extent_compression(leaf, fi, 0); btrfs_set_file_extent_encryption(leaf, fi, 0); btrfs_set_file_extent_other_encoding(leaf, fi, 0); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(root, &path); ins_key.objectid = disk_bytenr; ins_key.offset = num_bytes; ins_key.type = BTRFS_EXTENT_ITEM_KEY; ret = btrfs_insert_empty_item(trans, extent_root, &path, &ins_key, sizeof(*ei)); if (ret == 0) { leaf = path.nodes[0]; ei = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_extent_item); btrfs_set_extent_refs(leaf, ei, 0); btrfs_set_extent_generation(leaf, ei, trans->transid); btrfs_set_extent_flags(leaf, ei, BTRFS_EXTENT_FLAG_DATA); btrfs_mark_buffer_dirty(leaf); ret = btrfs_update_block_group(trans, root, disk_bytenr, num_bytes, 1, 0); if (ret) goto fail; } else if (ret != -EEXIST) {
/* * Unlink an inode, which will remove its backref and corresponding dir_index/ * dir_item if any of them exists. * * If an inode's nlink is reduced to 0 and 'add_orphan' is true, it will be * added to orphan inode and waiting to be deleted by next kernel mount. */ int btrfs_unlink(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 ino, u64 parent_ino, u64 index, const char *name, int namelen, int add_orphan) { struct btrfs_path *path; struct btrfs_key key; struct btrfs_inode_item *inode_item; struct btrfs_inode_ref *inode_ref; struct btrfs_dir_item *dir_item; u64 inode_size; u32 nlinks; int del_inode_ref = 0; int del_dir_item = 0; int del_dir_index = 0; int ret = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; /* check the ref and backref exists */ inode_ref = btrfs_lookup_inode_ref(trans, root, path, name, namelen, ino, parent_ino, index, 0); if (IS_ERR(inode_ref)) { ret = PTR_ERR(inode_ref); goto out; } if (inode_ref) del_inode_ref = 1; btrfs_release_path(path); dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino, name, namelen, 0); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (dir_item) del_dir_item = 1; btrfs_release_path(path); dir_item = btrfs_lookup_dir_index(NULL, root, path, parent_ino, name, namelen, index, 0); /* * Since lookup_dir_index() will return -ENOENT when not found, * we need to do extra check. */ if (IS_ERR(dir_item) && PTR_ERR(dir_item) == -ENOENT) dir_item = NULL; if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (dir_item) del_dir_index = 1; btrfs_release_path(path); if (!del_inode_ref && !del_dir_item && !del_dir_index) { /* All not found, shouldn't happen */ ret = -ENOENT; goto out; } if (del_inode_ref) { /* Only decrease nlink when deleting inode_ref */ key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret) { if (ret > 0) ret = -ENOENT; goto out; } inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); nlinks = btrfs_inode_nlink(path->nodes[0], inode_item); if (nlinks > 0) nlinks--; btrfs_set_inode_nlink(path->nodes[0], inode_item, nlinks); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); /* For nlinks == 0, add it to orphan list if needed */ if (nlinks == 0 && add_orphan) { ret = btrfs_add_orphan_item(trans, root, path, ino); if (ret < 0) goto out; btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } ret = btrfs_del_inode_ref(trans, root, name, namelen, ino, parent_ino, &index); if (ret < 0) goto out; } if (del_dir_index) { dir_item = btrfs_lookup_dir_index(trans, root, path, parent_ino, name, namelen, index, -1); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (!dir_item) { ret = -ENOENT; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, dir_item); if (ret) goto out; btrfs_release_path(path); /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); if (inode_size >= namelen) inode_size -= namelen; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } if (del_dir_item) { dir_item = btrfs_lookup_dir_item(trans, root, path, parent_ino, name, namelen, -1); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); goto out; } if (!dir_item) { ret = -ENOENT; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, dir_item); if (ret < 0) goto out; btrfs_release_path(path); /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); if (inode_size >= namelen) inode_size -= namelen; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } out: btrfs_free_path(path); return ret; }
/* * Add dir_item/index for 'parent_ino' if add_backref is true, also insert a * backref from the ino to parent dir and update the nlink(Kernel version does * not do this thing) * * Currently only supports adding link from an inode to another inode. */ int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 ino, u64 parent_ino, char *name, int namelen, u8 type, u64 *index, int add_backref) { struct btrfs_path *path; struct btrfs_key key; struct btrfs_inode_item *inode_item; u32 nlink; u64 inode_size; u64 ret_index = 0; int ret = 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; if (index && *index) { ret_index = *index; } else { ret = btrfs_find_free_dir_index(root, parent_ino, &ret_index); if (ret < 0) goto out; } ret = check_dir_conflict(root, name, namelen, parent_ino, ret_index); if (ret < 0) goto out; /* Add inode ref */ if (add_backref) { ret = btrfs_insert_inode_ref(trans, root, name, namelen, ino, parent_ino, ret_index); if (ret < 0) goto out; /* Update nlinks for the inode */ key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) { if (ret > 0) ret = -ENOENT; goto out; } inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); nlink = btrfs_inode_nlink(path->nodes[0], inode_item); nlink++; btrfs_set_inode_nlink(path->nodes[0], inode_item, nlink); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); } /* Add dir_item and dir_index */ key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_insert_dir_item(trans, root, name, namelen, parent_ino, &key, type, ret_index); if (ret < 0) goto out; /* Update inode size of the parent inode */ key.objectid = parent_ino; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(trans, root, &key, path, 1, 1); if (ret) goto out; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); inode_size = btrfs_inode_size(path->nodes[0], inode_item); inode_size += namelen * 2; btrfs_set_inode_size(path->nodes[0], inode_item, inode_size); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); out: btrfs_free_path(path); if (ret == 0 && index) *index = ret_index; return ret; }
static int do_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct btrfs_dir_item *di = NULL; struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_path *path; size_t name_len = strlen(name); int ret = 0; if (name_len + size > BTRFS_MAX_XATTR_SIZE(root)) return -ENOSPC; path = btrfs_alloc_path(); if (!path) return -ENOMEM; path->skip_release_on_error = 1; if (!value) { di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, name_len, -1); if (!di && (flags & XATTR_REPLACE)) ret = -ENODATA; else if (IS_ERR(di)) ret = PTR_ERR(di); else if (di) ret = btrfs_delete_one_dir_name(trans, root, path, di); goto out; } /* * For a replace we can't just do the insert blindly. * Do a lookup first (read-only btrfs_search_slot), and return if xattr * doesn't exist. If it exists, fall down below to the insert/replace * path - we can't race with a concurrent xattr delete, because the VFS * locks the inode's i_mutex before calling setxattr or removexattr. */ if (flags & XATTR_REPLACE) { ASSERT(inode_is_locked(inode)); di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode), name, name_len, 0); if (!di) ret = -ENODATA; else if (IS_ERR(di)) ret = PTR_ERR(di); if (ret) goto out; btrfs_release_path(path); di = NULL; } ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), name, name_len, value, size); if (ret == -EOVERFLOW) { /* * We have an existing item in a leaf, split_leaf couldn't * expand it. That item might have or not a dir_item that * matches our target xattr, so lets check. */ ret = 0; btrfs_assert_tree_locked(path->nodes[0]); di = btrfs_match_dir_item_name(root, path, name, name_len); if (!di && !(flags & XATTR_REPLACE)) { ret = -ENOSPC; goto out; } } else if (ret == -EEXIST) { ret = 0; di = btrfs_match_dir_item_name(root, path, name, name_len); ASSERT(di); /* logic error */ } else if (ret) { goto out; } if (di && (flags & XATTR_CREATE)) { ret = -EEXIST; goto out; } if (di) { /* * We're doing a replace, and it must be atomic, that is, at * any point in time we have either the old or the new xattr * value in the tree. We don't want readers (getxattr and * listxattrs) to miss a value, this is specially important * for ACLs. */ const int slot = path->slots[0]; struct extent_buffer *leaf = path->nodes[0]; const u16 old_data_len = btrfs_dir_data_len(leaf, di); const u32 item_size = btrfs_item_size_nr(leaf, slot); const u32 data_size = sizeof(*di) + name_len + size; struct btrfs_item *item; unsigned long data_ptr; char *ptr; if (size > old_data_len) { if (btrfs_leaf_free_space(root, leaf) < (size - old_data_len)) { ret = -ENOSPC; goto out; } } if (old_data_len + name_len + sizeof(*di) == item_size) { /* No other xattrs packed in the same leaf item. */ if (size > old_data_len) btrfs_extend_item(root, path, size - old_data_len); else if (size < old_data_len) btrfs_truncate_item(root, path, data_size, 1); } else { /* There are other xattrs packed in the same item. */ ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto out; btrfs_extend_item(root, path, data_size); } item = btrfs_item_nr(slot); ptr = btrfs_item_ptr(leaf, slot, char); ptr += btrfs_item_size(leaf, item) - data_size; di = (struct btrfs_dir_item *)ptr; btrfs_set_dir_data_len(leaf, di, size); data_ptr = ((unsigned long)(di + 1)) + name_len; write_extent_buffer(leaf, value, data_ptr, size); btrfs_mark_buffer_dirty(leaf); } else { /* * Insert, and we had space for the xattr, so path->slots[0] is * where our xattr dir_item is and btrfs_insert_xattr_item() * filled it. */ }
/* * insert a directory item in the tree, doing all the magic for * both indexes. 'dir' indicates which objectid to insert it into, * 'location' is the key to stuff into the directory item, 'type' is the * type of the inode we're pointing to, and 'index' is the sequence number * to use for the second index (if one is created). * Will return 0 or -ENOMEM */ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *name, int name_len, struct inode *dir, struct btrfs_key *location, u8 type, u64 index) { int ret = 0; int ret2 = 0; struct btrfs_path *path; struct btrfs_dir_item *dir_item; struct extent_buffer *leaf; unsigned long name_ptr; struct btrfs_key key; struct btrfs_disk_key disk_key; u32 data_size; key.objectid = btrfs_ino(dir); btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); key.offset = btrfs_name_hash(name, name_len); path = btrfs_alloc_path(); if (!path) return -ENOMEM; path->leave_spinning = 1; btrfs_cpu_key_to_disk(&disk_key, location); data_size = sizeof(*dir_item) + name_len; dir_item = insert_with_overflow(trans, root, path, &key, data_size, name, name_len); if (IS_ERR(dir_item)) { ret = PTR_ERR(dir_item); if (ret == -EEXIST) goto second_insert; goto out_free; } leaf = path->nodes[0]; btrfs_set_dir_item_key(leaf, dir_item, &disk_key); btrfs_set_dir_type(leaf, dir_item, type); btrfs_set_dir_data_len(leaf, dir_item, 0); btrfs_set_dir_name_len(leaf, dir_item, name_len); btrfs_set_dir_transid(leaf, dir_item, trans->transid); name_ptr = (unsigned long)(dir_item + 1); write_extent_buffer(leaf, name, name_ptr, name_len); btrfs_mark_buffer_dirty(leaf); second_insert: /* FIXME, use some real flag for selecting the extra index */ if (root == root->fs_info->tree_root) { ret = 0; goto out_free; } btrfs_release_path(path); ret2 = btrfs_insert_delayed_dir_index(trans, root, name, name_len, dir, &disk_key, type, index); out_free: btrfs_free_path(path); if (ret) return ret; if (ret2) return ret2; return 0; }
static int traverse_directory(struct btrfs_trans_handle *trans, struct btrfs_root *root, char *dir_name, struct directory_name_entry *dir_head, int out_fd) { int ret = 0; struct btrfs_inode_item cur_inode; struct btrfs_inode_item *inode_item; int count, i, dir_index_cnt; struct direct **files; struct stat st; struct directory_name_entry *dir_entry, *parent_dir_entry; struct direct *cur_file; ino_t parent_inum, cur_inum; ino_t highest_inum = 0; char *parent_dir_name; struct btrfs_path path; struct extent_buffer *leaf; struct btrfs_key root_dir_key; u64 root_dir_inode_size = 0; /* Add list for source directory */ dir_entry = malloc(sizeof(struct directory_name_entry)); dir_entry->dir_name = dir_name; dir_entry->path = strdup(dir_name); parent_inum = highest_inum + BTRFS_FIRST_FREE_OBJECTID; dir_entry->inum = parent_inum; list_add_tail(&dir_entry->list, &dir_head->list); btrfs_init_path(&path); root_dir_key.objectid = btrfs_root_dirid(&root->root_item); root_dir_key.offset = 0; btrfs_set_key_type(&root_dir_key, BTRFS_INODE_ITEM_KEY); ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1); if (ret) { fprintf(stderr, "root dir lookup error\n"); return -1; } leaf = path.nodes[0]; inode_item = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); root_dir_inode_size = calculate_dir_inode_size(dir_name); btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(&path); do { parent_dir_entry = list_entry(dir_head->list.next, struct directory_name_entry, list); list_del(&parent_dir_entry->list); parent_inum = parent_dir_entry->inum; parent_dir_name = parent_dir_entry->dir_name; if (chdir(parent_dir_entry->path)) { fprintf(stderr, "chdir error for %s\n", parent_dir_name); goto fail_no_files; } count = scandir(parent_dir_entry->path, &files, directory_select, NULL); if (count == -1) { fprintf(stderr, "scandir for %s failed: %s\n", parent_dir_name, strerror (errno)); goto fail; } for (i = 0; i < count; i++) { cur_file = files[i]; if (lstat(cur_file->d_name, &st) == -1) { fprintf(stderr, "lstat failed for file %s\n", cur_file->d_name); goto fail; } cur_inum = ++highest_inum + BTRFS_FIRST_FREE_OBJECTID; ret = add_directory_items(trans, root, cur_inum, parent_inum, cur_file->d_name, &st, &dir_index_cnt); if (ret) { fprintf(stderr, "add_directory_items failed\n"); goto fail; } ret = add_inode_items(trans, root, &st, cur_file->d_name, cur_inum, parent_inum, dir_index_cnt, &cur_inode); if (ret) { fprintf(stderr, "add_inode_items failed\n"); goto fail; } ret = add_xattr_item(trans, root, cur_inum, cur_file->d_name); if (ret) { fprintf(stderr, "add_xattr_item failed\n"); if(ret != -ENOTSUP) goto fail; } if (S_ISDIR(st.st_mode)) { dir_entry = malloc(sizeof(struct directory_name_entry)); dir_entry->dir_name = cur_file->d_name; dir_entry->path = make_path(parent_dir_entry->path, cur_file->d_name); dir_entry->inum = cur_inum; list_add_tail(&dir_entry->list, &dir_head->list); } else if (S_ISREG(st.st_mode)) { ret = add_file_items(trans, root, &cur_inode, cur_inum, parent_inum, &st, cur_file->d_name, out_fd); if (ret) { fprintf(stderr, "add_file_items failed\n"); goto fail; } } else if (S_ISLNK(st.st_mode)) { ret = add_symbolic_link(trans, root, cur_inum, cur_file->d_name); if (ret) { fprintf(stderr, "add_symbolic_link failed\n"); goto fail; } } } free_namelist(files, count); free(parent_dir_entry->path); free(parent_dir_entry); index_cnt = 2; } while (!list_empty(&dir_head->list)); return 0; fail: free_namelist(files, count); fail_no_files: free(parent_dir_entry->path); free(parent_dir_entry); return -1; }
int btrfs_dedup_enable(struct btrfs_fs_info *fs_info, u16 type, u16 backend, u64 blocksize, u64 limit) { struct btrfs_dedup_info *dedup_info; struct btrfs_root *dedup_root; struct btrfs_key key; struct btrfs_trans_handle *trans; struct btrfs_path *path; struct btrfs_dedup_status_item *status; int create_tree; u64 compat_ro_flag = btrfs_super_compat_ro_flags(fs_info->super_copy); int ret = 0; /* Sanity check */ if (blocksize > BTRFS_DEDUP_BLOCKSIZE_MAX || blocksize < BTRFS_DEDUP_BLOCKSIZE_MIN || blocksize < fs_info->tree_root->sectorsize || !is_power_of_2(blocksize)) return -EINVAL; if (type > ARRAY_SIZE(btrfs_dedup_sizes)) return -EINVAL; if (backend >= BTRFS_DEDUP_BACKEND_LAST) return -EINVAL; if (backend == BTRFS_DEDUP_BACKEND_INMEMORY && limit == 0) limit = 4096; /* default value */ if (backend == BTRFS_DEDUP_BACKEND_ONDISK && limit != 0) limit = 0; /* * If current fs doesn't support DEDUP feature, don't enable * on-disk dedup. */ if (!(compat_ro_flag & BTRFS_FEATURE_COMPAT_RO_DEDUP) && backend == BTRFS_DEDUP_BACKEND_ONDISK) return -EINVAL; /* Meaningless and unable to enable dedup for RO fs */ if (fs_info->sb->s_flags & MS_RDONLY) return -EINVAL; if (fs_info->dedup_info) { dedup_info = fs_info->dedup_info; /* Check if we are re-enable for different dedup config */ if (dedup_info->blocksize != blocksize || dedup_info->hash_type != type || dedup_info->backend != backend) { btrfs_dedup_disable(fs_info); goto enable; } /* On-fly limit change is OK */ mutex_lock(&dedup_info->lock); fs_info->dedup_info->limit_nr = limit; mutex_unlock(&dedup_info->lock); return 0; } enable: create_tree = compat_ro_flag & BTRFS_FEATURE_COMPAT_RO_DEDUP; ret = init_dedup_info(fs_info, type, backend, blocksize, limit); dedup_info = fs_info->dedup_info; if (ret < 0) goto out; if (!create_tree) goto out; /* Create dedup tree for status at least */ path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; goto out; } trans = btrfs_start_transaction(fs_info->tree_root, 2); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_free_path(path); goto out; } dedup_root = btrfs_create_tree(trans, fs_info, BTRFS_DEDUP_TREE_OBJECTID); if (IS_ERR(dedup_root)) { ret = PTR_ERR(dedup_root); btrfs_abort_transaction(trans, fs_info->tree_root, ret); btrfs_free_path(path); goto out; } dedup_info->dedup_root = dedup_root; key.objectid = 0; key.type = BTRFS_DEDUP_STATUS_ITEM_KEY; key.offset = 0; ret = btrfs_insert_empty_item(trans, dedup_root, path, &key, sizeof(*status)); if (ret < 0) { btrfs_abort_transaction(trans, fs_info->tree_root, ret); btrfs_free_path(path); goto out; } status = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_dedup_status_item); btrfs_set_dedup_status_blocksize(path->nodes[0], status, blocksize); btrfs_set_dedup_status_limit(path->nodes[0], status, limit); btrfs_set_dedup_status_hash_type(path->nodes[0], status, type); btrfs_set_dedup_status_backend(path->nodes[0], status, backend); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_free_path(path); ret = btrfs_commit_transaction(trans, fs_info->tree_root); out: if (ret < 0) { kfree(dedup_info); fs_info->dedup_info = NULL; } 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); }
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; }
int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { struct btrfs_root *root = fs_info->free_space_root; struct btrfs_free_space_info *info; struct btrfs_key key, found_key; struct extent_buffer *leaf; unsigned long *bitmap; char *bitmap_cursor; u64 start, end; u64 bitmap_range, i; u32 bitmap_size, flags, expected_extent_count; u32 extent_count = 0; int done = 0, nr; int ret; bitmap_size = free_space_bitmap_size(block_group->key.offset, block_group->sectorsize); bitmap = alloc_bitmap(bitmap_size); if (!bitmap) { ret = -ENOMEM; goto out; } start = block_group->key.objectid; end = block_group->key.objectid + block_group->key.offset; key.objectid = end - 1; key.type = (u8)-1; key.offset = (u64)-1; while (!done) { ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (ret) goto out; leaf = path->nodes[0]; nr = 0; path->slots[0]++; while (path->slots[0] > 0) { btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0] - 1); if (found_key.type == BTRFS_FREE_SPACE_INFO_KEY) { ASSERT(found_key.objectid == block_group->key.objectid); ASSERT(found_key.offset == block_group->key.offset); done = 1; break; } else if (found_key.type == BTRFS_FREE_SPACE_EXTENT_KEY) { u64 first, last; ASSERT(found_key.objectid >= start); ASSERT(found_key.objectid < end); ASSERT(found_key.objectid + found_key.offset <= end); first = div_u64(found_key.objectid - start, block_group->sectorsize); last = div_u64(found_key.objectid + found_key.offset - start, block_group->sectorsize); bitmap_set(bitmap, first, last - first); extent_count++; nr++; path->slots[0]--; } else { ASSERT(0); } } ret = btrfs_del_items(trans, root, path, path->slots[0], nr); if (ret) goto out; btrfs_release_path(path); } info = search_free_space_info(trans, fs_info, block_group, path, 1); if (IS_ERR(info)) { ret = PTR_ERR(info); goto out; } leaf = path->nodes[0]; flags = btrfs_free_space_flags(leaf, info); flags |= BTRFS_FREE_SPACE_USING_BITMAPS; btrfs_set_free_space_flags(leaf, info, flags); expected_extent_count = btrfs_free_space_extent_count(leaf, info); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); if (extent_count != expected_extent_count) { btrfs_err(fs_info, "incorrect extent count for %llu; counted %u, expected %u", block_group->key.objectid, extent_count, expected_extent_count); ASSERT(0); ret = -EIO; goto out; } bitmap_cursor = (char *)bitmap; bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS; i = start; while (i < end) { unsigned long ptr; u64 extent_size; u32 data_size; extent_size = min(end - i, bitmap_range); data_size = free_space_bitmap_size(extent_size, block_group->sectorsize); key.objectid = i; key.type = BTRFS_FREE_SPACE_BITMAP_KEY; key.offset = extent_size; ret = btrfs_insert_empty_item(trans, root, path, &key, data_size); if (ret) goto out; leaf = path->nodes[0]; ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); write_extent_buffer(leaf, bitmap_cursor, ptr, data_size); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); i += extent_size; bitmap_cursor += data_size; } ret = 0; out: kvfree(bitmap); if (ret) btrfs_abort_transaction(trans, ret); return ret; }