aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2007-03-27 11:05:53 -0400
committerDavid Woodhouse <dwmw2@hera.kernel.org>2007-03-27 11:05:53 -0400
commitf4b9aa8d3b877d0a6044a6d6d9a44b29cab9e265 (patch)
tree227c83dac6b68407e3396632866f5564391ca15d /fs/btrfs
parent71951f35a6e413f2bfbd41829af8cf10a890aeb6 (diff)
downloadkernel_samsung_smdk4412-f4b9aa8d3b877d0a6044a6d6d9a44b29cab9e265.zip
kernel_samsung_smdk4412-f4b9aa8d3b877d0a6044a6d6d9a44b29cab9e265.tar.gz
kernel_samsung_smdk4412-f4b9aa8d3b877d0a6044a6d6d9a44b29cab9e265.tar.bz2
btrfs_truncate
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/extent-tree.c25
-rw-r--r--fs/btrfs/super.c107
2 files changed, 114 insertions, 18 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 4d4fc48..176c6dc 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -171,20 +171,21 @@ static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
struct btrfs_header *header;
struct buffer_head *bh;
- bh = sb_find_get_block(root->fs_info->sb, blocknr);
- if (bh) {
- header = btrfs_buffer_header(bh);
- if (btrfs_header_generation(header) ==
- root->fs_info->running_transaction->transid) {
+ if (!pending) {
+ bh = sb_find_get_block(root->fs_info->sb, blocknr);
+ if (bh) {
+ header = btrfs_buffer_header(bh);
+ if (btrfs_header_generation(header) ==
+ root->fs_info->running_transaction->transid) {
+ brelse(bh);
+ return 0;
+ }
brelse(bh);
- return 0;
}
- brelse(bh);
- }
- if (pending)
- err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
- else
err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
+ } else {
+ err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
+ }
BUG_ON(err);
return 0;
}
@@ -223,6 +224,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
BUG_ON(ei->refs == 0);
refs = btrfs_extent_refs(ei) - 1;
btrfs_set_extent_refs(ei, refs);
+ mark_buffer_dirty(path.nodes[0]);
if (refs == 0) {
u64 super_blocks_used;
@@ -240,7 +242,6 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
if (ret)
BUG();
}
- mark_buffer_dirty(path.nodes[0]);
btrfs_release_path(extent_root, &path);
finish_current_insert(trans, extent_root);
return ret;
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 487da5a..fd3d9d6 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -28,11 +28,15 @@ static void btrfs_read_locked_inode(struct inode *inode)
struct btrfs_inode_item *inode_item;
struct btrfs_root *root = btrfs_sb(inode->i_sb);
int ret;
+
btrfs_init_path(&path);
+ mutex_lock(&root->fs_info->fs_mutex);
+
ret = btrfs_lookup_inode(NULL, root, &path, inode->i_ino, 0);
if (ret) {
- make_bad_inode(inode);
btrfs_release_path(root, &path);
+ mutex_unlock(&root->fs_info->fs_mutex);
+ make_bad_inode(inode);
return;
}
inode_item = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]),
@@ -53,6 +57,7 @@ static void btrfs_read_locked_inode(struct inode *inode)
inode->i_blocks = btrfs_inode_nblocks(inode_item);
inode->i_generation = btrfs_inode_generation(inode_item);
btrfs_release_path(root, &path);
+ mutex_unlock(&root->fs_info->fs_mutex);
switch (inode->i_mode & S_IFMT) {
#if 0
default:
@@ -151,20 +156,85 @@ error:
return ret;
}
+static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode *inode)
+{
+ int ret;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_disk_key *found_key;
+ struct btrfs_leaf *leaf;
+ struct btrfs_file_extent_item *fi;
+ u64 extent_start;
+ u64 extent_num_blocks;
+
+ /* FIXME, add redo link to tree so we don't leak on crash */
+ key.objectid = inode->i_ino;
+ key.offset = (u64)-1;
+ key.flags = 0;
+ btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+ while(1) {
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
+ if (ret < 0) {
+ btrfs_release_path(root, &path);
+ goto error;
+ }
+ if (ret > 0) {
+ BUG_ON(path.slots[0] == 0);
+ path.slots[0]--;
+ }
+ leaf = btrfs_buffer_leaf(path.nodes[0]);
+ found_key = &leaf->items[path.slots[0]].key;
+ if (btrfs_disk_key_objectid(found_key) != inode->i_ino)
+ break;
+ if (btrfs_disk_key_type(found_key) != BTRFS_EXTENT_DATA_KEY)
+ break;
+ if (btrfs_disk_key_offset(found_key) < inode->i_size)
+ break;
+ /* FIXME: add extent truncation */
+ if (btrfs_disk_key_offset(found_key) < inode->i_size)
+ break;
+ fi = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]),
+ path.slots[0],
+ struct btrfs_file_extent_item);
+ extent_start = btrfs_file_extent_disk_blocknr(fi);
+ extent_num_blocks = btrfs_file_extent_disk_num_blocks(fi);
+ key.offset = btrfs_disk_key_offset(found_key) - 1;
+ ret = btrfs_del_item(trans, root, &path);
+ BUG_ON(ret);
+ inode->i_blocks -= btrfs_file_extent_num_blocks(fi) >> 9;
+ btrfs_release_path(root, &path);
+ ret = btrfs_free_extent(trans, root, extent_start,
+ extent_num_blocks, 0);
+ BUG_ON(ret);
+ if (btrfs_disk_key_offset(found_key) == 0)
+ break;
+ }
+ btrfs_release_path(root, &path);
+ ret = 0;
+error:
+ return ret;
+}
+
static void btrfs_delete_inode(struct inode *inode)
{
struct btrfs_trans_handle *trans;
struct btrfs_root *root = btrfs_sb(inode->i_sb);
+ int ret;
+
truncate_inode_pages(&inode->i_data, 0);
if (is_bad_inode(inode)) {
goto no_delete;
}
inode->i_size = 0;
- if (inode->i_blocks)
- WARN_ON(1);
-
mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1);
+ if (S_ISREG(inode->i_mode)) {
+ ret = btrfs_truncate_in_trans(trans, root, inode);
+ BUG_ON(ret);
+ }
btrfs_free_inode(trans, root, inode);
btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex);
@@ -173,7 +243,6 @@ no_delete:
clear_inode(inode);
}
-
static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
ino_t *ino)
{
@@ -688,6 +757,8 @@ allocate:
err = ret;
goto out;
}
+ inode->i_blocks += inode->i_sb->s_blocksize >> 9;
+ set_buffer_new(result);
map_bh(result, inode->i_sb, blocknr);
out:
@@ -724,6 +795,30 @@ static int btrfs_writepage(struct page *page, struct writeback_control *wbc)
return nobh_writepage(page, btrfs_get_block, wbc);
}
+static void btrfs_truncate(struct inode *inode)
+{
+ struct btrfs_root *root = btrfs_sb(inode->i_sb);
+ int ret;
+ struct btrfs_trans_handle *trans;
+
+ if (!S_ISREG(inode->i_mode))
+ return;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return;
+
+ nobh_truncate_page(inode->i_mapping, inode->i_size);
+
+ /* FIXME, add redo link to tree so we don't leak on crash */
+ mutex_lock(&root->fs_info->fs_mutex);
+ trans = btrfs_start_transaction(root, 1);
+ ret = btrfs_truncate_in_trans(trans, root, inode);
+ BUG_ON(ret);
+ ret = btrfs_end_transaction(trans, root);
+ BUG_ON(ret);
+ mutex_unlock(&root->fs_info->fs_mutex);
+ mark_inode_dirty(inode);
+}
+
static int btrfs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
@@ -772,7 +867,7 @@ static struct address_space_operations btrfs_aops = {
};
static struct inode_operations btrfs_file_inode_operations = {
- .truncate = NULL,
+ .truncate = btrfs_truncate,
};
static struct file_operations btrfs_file_operations = {