mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-07 07:08:07 +00:00
btrfs_ioctl_clone: Move clone code into it's own function
There's some 250+ lines here that are easily encapsulated into their own function. I don't change how anything works here, just create and document the new btrfs_clone() function from btrfs_ioctl_clone() code. Signed-off-by: Mark Fasheh <mfasheh@suse.de> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
parent
77fe20dc62
commit
32b7c687c5
1 changed files with 139 additions and 115 deletions
254
fs/btrfs/ioctl.c
254
fs/btrfs/ioctl.c
|
@ -2490,136 +2490,43 @@ static inline void lock_extent_range(struct inode *inode, u64 off, u64 len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
/**
|
||||||
u64 off, u64 olen, u64 destoff)
|
* btrfs_clone() - clone a range from inode file to another
|
||||||
|
*
|
||||||
|
* @src: Inode to clone from
|
||||||
|
* @inode: Inode to clone to
|
||||||
|
* @off: Offset within source to start clone from
|
||||||
|
* @olen: Original length, passed by user, of range to clone
|
||||||
|
* @olen_aligned: Block-aligned value of olen, extent_same uses
|
||||||
|
* identical values here
|
||||||
|
* @destoff: Offset within @inode to start clone
|
||||||
|
*/
|
||||||
|
static int btrfs_clone(struct inode *src, struct inode *inode,
|
||||||
|
u64 off, u64 olen, u64 olen_aligned, u64 destoff)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(file);
|
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
struct fd src_file;
|
struct btrfs_path *path = NULL;
|
||||||
struct inode *src;
|
|
||||||
struct btrfs_trans_handle *trans;
|
|
||||||
struct btrfs_path *path;
|
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
char *buf;
|
struct btrfs_trans_handle *trans;
|
||||||
|
char *buf = NULL;
|
||||||
struct btrfs_key key;
|
struct btrfs_key key;
|
||||||
u32 nritems;
|
u32 nritems;
|
||||||
int slot;
|
int slot;
|
||||||
int ret;
|
int ret;
|
||||||
u64 len = olen;
|
u64 len = olen_aligned;
|
||||||
u64 bs = root->fs_info->sb->s_blocksize;
|
|
||||||
int same_inode = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO:
|
|
||||||
* - split compressed inline extents. annoying: we need to
|
|
||||||
* decompress into destination's address_space (the file offset
|
|
||||||
* may change, so source mapping won't do), then recompress (or
|
|
||||||
* otherwise reinsert) a subrange.
|
|
||||||
* - allow ranges within the same file to be cloned (provided
|
|
||||||
* they don't overlap)?
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* the destination must be opened for writing */
|
|
||||||
if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (btrfs_root_readonly(root))
|
|
||||||
return -EROFS;
|
|
||||||
|
|
||||||
ret = mnt_want_write_file(file);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
src_file = fdget(srcfd);
|
|
||||||
if (!src_file.file) {
|
|
||||||
ret = -EBADF;
|
|
||||||
goto out_drop_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = -EXDEV;
|
|
||||||
if (src_file.file->f_path.mnt != file->f_path.mnt)
|
|
||||||
goto out_fput;
|
|
||||||
|
|
||||||
src = file_inode(src_file.file);
|
|
||||||
|
|
||||||
ret = -EINVAL;
|
|
||||||
if (src == inode)
|
|
||||||
same_inode = 1;
|
|
||||||
|
|
||||||
/* the src must be open for reading */
|
|
||||||
if (!(src_file.file->f_mode & FMODE_READ))
|
|
||||||
goto out_fput;
|
|
||||||
|
|
||||||
/* don't make the dst file partly checksummed */
|
|
||||||
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
|
|
||||||
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
|
|
||||||
goto out_fput;
|
|
||||||
|
|
||||||
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)
|
|
||||||
goto out_fput;
|
|
||||||
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
buf = vmalloc(btrfs_level_size(root, 0));
|
buf = vmalloc(btrfs_level_size(root, 0));
|
||||||
if (!buf)
|
if (!buf)
|
||||||
goto out_fput;
|
return ret;
|
||||||
|
|
||||||
path = btrfs_alloc_path();
|
path = btrfs_alloc_path();
|
||||||
if (!path) {
|
if (!path) {
|
||||||
vfree(buf);
|
vfree(buf);
|
||||||
goto out_fput;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
path->reada = 2;
|
path->reada = 2;
|
||||||
|
|
||||||
if (!same_inode) {
|
|
||||||
if (inode < src) {
|
|
||||||
mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
|
|
||||||
mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
|
|
||||||
} else {
|
|
||||||
mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
|
|
||||||
mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mutex_lock(&src->i_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* determine range to clone */
|
|
||||||
ret = -EINVAL;
|
|
||||||
if (off + len > src->i_size || off + len < off)
|
|
||||||
goto out_unlock;
|
|
||||||
if (len == 0)
|
|
||||||
olen = len = src->i_size - off;
|
|
||||||
/* if we extend to eof, continue to block boundary */
|
|
||||||
if (off + len == src->i_size)
|
|
||||||
len = ALIGN(src->i_size, bs) - off;
|
|
||||||
|
|
||||||
/* verify the end result is block aligned */
|
|
||||||
if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs) ||
|
|
||||||
!IS_ALIGNED(destoff, bs))
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
/* verify if ranges are overlapped within the same file */
|
|
||||||
if (same_inode) {
|
|
||||||
if (destoff + len > off && destoff < off + len)
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destoff > inode->i_size) {
|
|
||||||
ret = btrfs_cont_expand(inode, inode->i_size, destoff);
|
|
||||||
if (ret)
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* truncate page cache pages from target inode range */
|
|
||||||
truncate_inode_pages_range(&inode->i_data, destoff,
|
|
||||||
PAGE_CACHE_ALIGN(destoff + len) - 1);
|
|
||||||
|
|
||||||
lock_extent_range(src, off, len);
|
|
||||||
|
|
||||||
/* clone data */
|
/* clone data */
|
||||||
key.objectid = btrfs_ino(src);
|
key.objectid = btrfs_ino(src);
|
||||||
key.type = BTRFS_EXTENT_DATA_KEY;
|
key.type = BTRFS_EXTENT_DATA_KEY;
|
||||||
|
@ -2865,15 +2772,132 @@ next:
|
||||||
key.offset++;
|
key.offset++;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
btrfs_release_path(path);
|
btrfs_release_path(path);
|
||||||
|
btrfs_free_path(path);
|
||||||
|
vfree(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||||
|
u64 off, u64 olen, u64 destoff)
|
||||||
|
{
|
||||||
|
struct inode *inode = fdentry(file)->d_inode;
|
||||||
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
|
struct fd src_file;
|
||||||
|
struct inode *src;
|
||||||
|
int ret;
|
||||||
|
u64 len = olen;
|
||||||
|
u64 bs = root->fs_info->sb->s_blocksize;
|
||||||
|
int same_inode = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* - split compressed inline extents. annoying: we need to
|
||||||
|
* decompress into destination's address_space (the file offset
|
||||||
|
* may change, so source mapping won't do), then recompress (or
|
||||||
|
* otherwise reinsert) a subrange.
|
||||||
|
* - allow ranges within the same file to be cloned (provided
|
||||||
|
* they don't overlap)?
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* the destination must be opened for writing */
|
||||||
|
if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (btrfs_root_readonly(root))
|
||||||
|
return -EROFS;
|
||||||
|
|
||||||
|
ret = mnt_want_write_file(file);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
src_file = fdget(srcfd);
|
||||||
|
if (!src_file.file) {
|
||||||
|
ret = -EBADF;
|
||||||
|
goto out_drop_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = -EXDEV;
|
||||||
|
if (src_file.file->f_path.mnt != file->f_path.mnt)
|
||||||
|
goto out_fput;
|
||||||
|
|
||||||
|
src = file_inode(src_file.file);
|
||||||
|
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (src == inode)
|
||||||
|
same_inode = 1;
|
||||||
|
|
||||||
|
/* the src must be open for reading */
|
||||||
|
if (!(src_file.file->f_mode & FMODE_READ))
|
||||||
|
goto out_fput;
|
||||||
|
|
||||||
|
/* don't make the dst file partly checksummed */
|
||||||
|
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
|
||||||
|
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
|
||||||
|
goto out_fput;
|
||||||
|
|
||||||
|
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)
|
||||||
|
goto out_fput;
|
||||||
|
|
||||||
|
if (!same_inode) {
|
||||||
|
if (inode < src) {
|
||||||
|
mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
|
||||||
|
mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
|
||||||
|
} else {
|
||||||
|
mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
|
||||||
|
mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mutex_lock(&src->i_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine range to clone */
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (off + len > src->i_size || off + len < off)
|
||||||
|
goto out_unlock;
|
||||||
|
if (len == 0)
|
||||||
|
olen = len = src->i_size - off;
|
||||||
|
/* if we extend to eof, continue to block boundary */
|
||||||
|
if (off + len == src->i_size)
|
||||||
|
len = ALIGN(src->i_size, bs) - off;
|
||||||
|
|
||||||
|
/* verify the end result is block aligned */
|
||||||
|
if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs) ||
|
||||||
|
!IS_ALIGNED(destoff, bs))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
/* verify if ranges are overlapped within the same file */
|
||||||
|
if (same_inode) {
|
||||||
|
if (destoff + len > off && destoff < off + len)
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destoff > inode->i_size) {
|
||||||
|
ret = btrfs_cont_expand(inode, inode->i_size, destoff);
|
||||||
|
if (ret)
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* truncate page cache pages from target inode range */
|
||||||
|
truncate_inode_pages_range(&inode->i_data, destoff,
|
||||||
|
PAGE_CACHE_ALIGN(destoff + len) - 1);
|
||||||
|
|
||||||
|
lock_extent_range(src, off, len);
|
||||||
|
|
||||||
|
ret = btrfs_clone(src, inode, off, olen, len, destoff);
|
||||||
|
|
||||||
unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
|
unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&src->i_mutex);
|
mutex_unlock(&src->i_mutex);
|
||||||
if (!same_inode)
|
if (!same_inode)
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
vfree(buf);
|
|
||||||
btrfs_free_path(path);
|
|
||||||
out_fput:
|
out_fput:
|
||||||
fdput(src_file);
|
fdput(src_file);
|
||||||
out_drop_write:
|
out_drop_write:
|
||||||
|
|
Loading…
Add table
Reference in a new issue