#include <linux/bit_spinlock.h>
#include <linux/version.h>
#include <linux/xattr.h>
+#include <linux/vmalloc.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
#include "ioctl.h"
#include "print-tree.h"
#include "volumes.h"
+#include "locking.h"
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
unsigned long nr = 1;
- mutex_lock(&root->fs_info->fs_mutex);
ret = btrfs_check_free_space(root, 1, 0);
if (ret)
goto fail_commit;
if (ret)
goto fail;
- leaf = __btrfs_alloc_free_block(trans, root, root->leafsize,
- objectid, trans->transid, 0, 0,
- 0, 0);
- if (IS_ERR(leaf))
- return PTR_ERR(leaf);
+ leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
+ objectid, trans->transid, 0, 0,
+ 0, 0);
+ if (IS_ERR(leaf)) {
+ ret = PTR_ERR(leaf);
+ goto fail;
+ }
btrfs_set_header_nritems(leaf, 0);
btrfs_set_header_level(leaf, 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;
dir = root->fs_info->sb->s_root->d_inode;
ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root,
name, namelen, dir->i_ino, &key,
- BTRFS_FT_DIR);
+ BTRFS_FT_DIR, 0);
if (ret)
goto fail;
ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root,
name, namelen, objectid,
- root->fs_info->sb->s_root->d_inode->i_ino);
+ root->fs_info->sb->s_root->d_inode->i_ino, 0);
if (ret)
goto fail;
if (err && !ret)
ret = err;
fail_commit:
- mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root, nr);
- btrfs_throttle(root);
return ret;
}
if (!root->ref_cows)
return -EINVAL;
- mutex_lock(&root->fs_info->fs_mutex);
ret = btrfs_check_free_space(root, 1, 0);
if (ret)
goto fail_unlock;
err = btrfs_commit_transaction(trans, root);
fail_unlock:
- mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root, nr);
- btrfs_throttle(root);
return ret;
}
struct inode *inode = fdentry(file)->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+ struct btrfs_ordered_extent *ordered;
struct page *page;
unsigned long last_index;
unsigned long ra_pages = root->fs_info->bdi.ra_pages;
unsigned long i;
int ret;
- mutex_lock(&root->fs_info->fs_mutex);
ret = btrfs_check_free_space(root, inode->i_size, 0);
- mutex_unlock(&root->fs_info->fs_mutex);
if (ret)
return -ENOSPC;
min(last_index, i + ra_pages - 1));
}
total_read++;
+again:
page = grab_cache_page(inode->i_mapping, i);
if (!page)
goto out_unlock;
}
}
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
- ClearPageDirty(page);
-#else
- cancel_dirty_page(page, PAGE_CACHE_SIZE);
-#endif
wait_on_page_writeback(page);
- set_page_extent_mapped(page);
page_start = (u64)page->index << PAGE_CACHE_SHIFT;
page_end = page_start + PAGE_CACHE_SIZE - 1;
-
lock_extent(io_tree, page_start, page_end, GFP_NOFS);
- set_extent_delalloc(io_tree, page_start,
- page_end, GFP_NOFS);
+
+ ordered = btrfs_lookup_ordered_extent(inode, page_start);
+ if (ordered) {
+ unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
+ unlock_page(page);
+ page_cache_release(page);
+ btrfs_start_ordered_extent(inode, ordered, 1);
+ btrfs_put_ordered_extent(ordered);
+ goto again;
+ }
+ set_page_extent_mapped(page);
+
+ /*
+ * this makes sure page_mkwrite is called on the
+ * page if it is dirtied again later
+ */
+ clear_page_dirty_for_io(page);
+
+ btrfs_set_extent_delalloc(inode, page_start, page_end);
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
set_page_dirty(page);
ret = -EFAULT;
goto out;
}
+
+ vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
namelen = strlen(vol_args->name);
- if (namelen > BTRFS_VOL_NAME_MAX) {
- ret = -EINVAL;
- goto out;
- }
- mutex_lock(&root->fs_info->fs_mutex);
+ mutex_lock(&root->fs_info->volume_mutex);
sizestr = vol_args->name;
devstr = strchr(sizestr, ':');
if (devstr) {
}
out_unlock:
- mutex_unlock(&root->fs_info->fs_mutex);
+ mutex_unlock(&root->fs_info->volume_mutex);
out:
kfree(vol_args);
return ret;
goto out;
}
+ vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
namelen = strlen(vol_args->name);
- if (namelen > BTRFS_VOL_NAME_MAX) {
- ret = -EINVAL;
- goto out;
- }
if (strchr(vol_args->name, '/')) {
ret = -EINVAL;
goto out;
}
root_dirid = root->fs_info->sb->s_root->d_inode->i_ino,
- mutex_lock(&root->fs_info->fs_mutex);
di = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root,
path, root_dirid,
vol_args->name, namelen, 0);
- mutex_unlock(&root->fs_info->fs_mutex);
btrfs_free_path(path);
if (di && !IS_ERR(di)) {
goto out;
}
+ mutex_lock(&root->fs_info->drop_mutex);
if (root == root->fs_info->tree_root)
ret = create_subvol(root, vol_args->name, namelen);
else
ret = create_snapshot(root, vol_args->name, namelen);
+ mutex_unlock(&root->fs_info->drop_mutex);
out:
kfree(vol_args);
return ret;
switch (inode->i_mode & S_IFMT) {
case S_IFDIR:
- mutex_lock(&root->fs_info->fs_mutex);
btrfs_defrag_root(root, 0);
btrfs_defrag_root(root->fs_info->extent_root, 0);
- mutex_unlock(&root->fs_info->fs_mutex);
break;
case S_IFREG:
btrfs_defrag_file(file);
ret = -EFAULT;
goto out;
}
+ vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
ret = btrfs_init_new_device(root, vol_args->name);
out:
ret = -EFAULT;
goto out;
}
+ vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
ret = btrfs_rm_device(root, vol_args->name);
out:
return ret;
}
-int dup_item_to_inode(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
- struct btrfs_path *path,
- struct extent_buffer *leaf,
- int slot,
- struct btrfs_key *key,
- u64 destino)
-{
- char *dup;
- int len = btrfs_item_size_nr(leaf, slot);
- struct btrfs_key ckey = *key;
- int ret = 0;
-
- dup = kmalloc(len, GFP_NOFS);
- if (!dup)
- return -ENOMEM;
-
- read_extent_buffer(leaf, dup, btrfs_item_ptr_offset(leaf, slot), len);
- btrfs_release_path(root, path);
-
- ckey.objectid = destino;
- ret = btrfs_insert_item(trans, root, &ckey, dup, len);
- kfree(dup);
- return ret;
-}
-
long btrfs_ioctl_clone(struct file *file, unsigned long src_fd)
{
struct inode *inode = fdentry(file)->d_inode;
struct file *src_file;
struct inode *src;
struct btrfs_trans_handle *trans;
- int ret;
- u64 pos;
+ struct btrfs_ordered_extent *ordered;
struct btrfs_path *path;
- struct btrfs_key key;
struct extent_buffer *leaf;
+ char *buf;
+ struct btrfs_key key;
+ struct btrfs_key new_key;
+ u32 size;
u32 nritems;
int slot;
+ int ret;
src_file = fget(src_fd);
if (!src_file)
return -EBADF;
src = src_file->f_dentry->d_inode;
+ ret = -EISDIR;
+ if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
+ goto out_fput;
+
ret = -EXDEV;
- if (src->i_sb != inode->i_sb)
+ if (src->i_sb != inode->i_sb || BTRFS_I(src)->root != root)
+ goto out_fput;
+
+ ret = -ENOMEM;
+ buf = vmalloc(btrfs_level_size(root, 0));
+ if (!buf)
+ goto out_fput;
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ vfree(buf);
goto out_fput;
+ }
+ path->reada = 2;
if (inode < src) {
mutex_lock(&inode->i_mutex);
/* do any pending delalloc/csum calc on src, one way or
another, and lock file content */
while (1) {
- filemap_write_and_wait(src->i_mapping);
lock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS);
- if (BTRFS_I(src)->delalloc_bytes == 0)
+ ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1);
+ if (BTRFS_I(src)->delalloc_bytes == 0 && !ordered)
break;
unlock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS);
+ if (ordered)
+ btrfs_put_ordered_extent(ordered);
+ btrfs_wait_ordered_range(src, 0, (u64)-1);
}
- mutex_lock(&root->fs_info->fs_mutex);
- trans = btrfs_start_transaction(root, 0);
- path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
- key.offset = 0;
- key.type = BTRFS_EXTENT_DATA_KEY;
+ trans = btrfs_start_transaction(root, 1);
+ BUG_ON(!trans);
+
key.objectid = src->i_ino;
- pos = 0;
- path->reada = 2;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = 0;
while (1) {
/*
if (ret < 0)
goto out;
- if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+ nritems = btrfs_header_nritems(path->nodes[0]);
+ if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path);
if (ret < 0)
goto out;
if (ret > 0)
break;
+ nritems = btrfs_header_nritems(path->nodes[0]);
}
leaf = path->nodes[0];
slot = path->slots[0];
- btrfs_item_key_to_cpu(leaf, &key, slot);
- nritems = btrfs_header_nritems(leaf);
+ btrfs_item_key_to_cpu(leaf, &key, slot);
if (btrfs_key_type(&key) > BTRFS_CSUM_ITEM_KEY ||
key.objectid != src->i_ino)
break;
if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) {
struct btrfs_file_extent_item *extent;
int found_type;
- pos = key.offset;
+
extent = btrfs_item_ptr(leaf, slot,
struct btrfs_file_extent_item);
found_type = btrfs_file_extent_type(leaf, extent);
if (found_type == BTRFS_FILE_EXTENT_REG) {
- u64 len = btrfs_file_extent_num_bytes(leaf,
- extent);
u64 ds = btrfs_file_extent_disk_bytenr(leaf,
extent);
u64 dl = btrfs_file_extent_disk_num_bytes(leaf,
extent);
- u64 off = btrfs_file_extent_offset(leaf,
- extent);
- btrfs_insert_file_extent(trans, root,
- inode->i_ino, pos,
- ds, dl, len, off);
/* ds == 0 means there's a hole */
if (ds != 0) {
- btrfs_inc_extent_ref(trans, root,
+ ret = btrfs_inc_extent_ref(trans, root,
ds, dl,
root->root_key.objectid,
trans->transid,
- inode->i_ino, pos);
+ inode->i_ino, key.offset);
+ if (ret)
+ goto out;
}
- pos = key.offset + len;
- } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
- ret = dup_item_to_inode(trans, root, path,
- leaf, slot, &key,
- inode->i_ino);
- if (ret)
- goto out;
- pos = key.offset + btrfs_item_size_nr(leaf,
- slot);
}
- } else if (btrfs_key_type(&key) == BTRFS_CSUM_ITEM_KEY) {
- ret = dup_item_to_inode(trans, root, path, leaf,
- slot, &key, inode->i_ino);
+ }
- if (ret)
- goto out;
+ if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY ||
+ btrfs_key_type(&key) == BTRFS_CSUM_ITEM_KEY) {
+ size = btrfs_item_size_nr(leaf, slot);
+ read_extent_buffer(leaf, buf,
+ btrfs_item_ptr_offset(leaf, slot),
+ size);
+ btrfs_release_path(root, path);
+ memcpy(&new_key, &key, sizeof(new_key));
+ new_key.objectid = inode->i_ino;
+ ret = btrfs_insert_item(trans, root, &new_key,
+ buf, size);
+ BUG_ON(ret);
+ } else {
+ btrfs_release_path(root, path);
}
key.offset++;
- btrfs_release_path(root, path);
}
-
ret = 0;
out:
- btrfs_free_path(path);
-
- inode->i_blocks = src->i_blocks;
- i_size_write(inode, src->i_size);
- btrfs_update_inode(trans, root, inode);
-
- unlock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS);
-
+ btrfs_release_path(root, path);
+ if (ret == 0) {
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = src->i_blocks;
+ btrfs_i_size_write(inode, src->i_size);
+ BTRFS_I(inode)->flags = BTRFS_I(src)->flags;
+ ret = btrfs_update_inode(trans, root, inode);
+ }
btrfs_end_transaction(trans, root);
- mutex_unlock(&root->fs_info->fs_mutex);
-
+ unlock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS);
+ if (ret)
+ vmtruncate(inode, 0);
out_unlock:
mutex_unlock(&src->i_mutex);
mutex_unlock(&inode->i_mutex);
+ vfree(buf);
+ btrfs_free_path(path);
out_fput:
fput(src_file);
return ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- mutex_lock(&root->fs_info->fs_mutex);
if (file->private_data) {
ret = -EINPROGRESS;
goto out;
}
- trans = btrfs_start_transaction(root, 0);
+
+ mutex_lock(&root->fs_info->trans_mutex);
+ root->fs_info->open_ioctl_trans++;
+ mutex_unlock(&root->fs_info->trans_mutex);
+
+ trans = btrfs_start_ioctl_transaction(root, 0);
if (trans)
file->private_data = trans;
else
ret = -ENOMEM;
/*printk(KERN_INFO "btrfs_ioctl_trans_start on %p\n", file);*/
out:
- mutex_unlock(&root->fs_info->fs_mutex);
return ret;
}
struct btrfs_trans_handle *trans;
int ret = 0;
- mutex_lock(&root->fs_info->fs_mutex);
trans = file->private_data;
if (!trans) {
ret = -EINVAL;
goto out;
}
btrfs_end_transaction(trans, root);
- file->private_data = 0;
+ file->private_data = NULL;
+
+ mutex_lock(&root->fs_info->trans_mutex);
+ root->fs_info->open_ioctl_trans--;
+ mutex_unlock(&root->fs_info->trans_mutex);
+
out:
- mutex_unlock(&root->fs_info->fs_mutex);
return ret;
}
case BTRFS_IOC_TRANS_END:
return btrfs_ioctl_trans_end(file);
case BTRFS_IOC_SYNC:
+ btrfs_start_delalloc_inodes(root);
btrfs_sync_fs(file->f_dentry->d_sb, 1);
return 0;
}