aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r--fs/btrfs/ioctl.c40
1 files changed, 31 insertions, 9 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 970977aab224..538f65a79ec5 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2177,6 +2177,11 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
2177 if (!(src_file->f_mode & FMODE_READ)) 2177 if (!(src_file->f_mode & FMODE_READ))
2178 goto out_fput; 2178 goto out_fput;
2179 2179
2180 /* don't make the dst file partly checksummed */
2181 if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
2182 (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
2183 goto out_fput;
2184
2180 ret = -EISDIR; 2185 ret = -EISDIR;
2181 if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode)) 2186 if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
2182 goto out_fput; 2187 goto out_fput;
@@ -2220,6 +2225,16 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
2220 !IS_ALIGNED(destoff, bs)) 2225 !IS_ALIGNED(destoff, bs))
2221 goto out_unlock; 2226 goto out_unlock;
2222 2227
2228 if (destoff > inode->i_size) {
2229 ret = btrfs_cont_expand(inode, inode->i_size, destoff);
2230 if (ret)
2231 goto out_unlock;
2232 }
2233
2234 /* truncate page cache pages from target inode range */
2235 truncate_inode_pages_range(&inode->i_data, destoff,
2236 PAGE_CACHE_ALIGN(destoff + len) - 1);
2237
2223 /* do any pending delalloc/csum calc on src, one way or 2238 /* do any pending delalloc/csum calc on src, one way or
2224 another, and lock file content */ 2239 another, and lock file content */
2225 while (1) { 2240 while (1) {
@@ -2236,10 +2251,6 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
2236 btrfs_wait_ordered_range(src, off, len); 2251 btrfs_wait_ordered_range(src, off, len);
2237 } 2252 }
2238 2253
2239 /* truncate page cache pages from target inode range */
2240 truncate_inode_pages_range(&inode->i_data, off,
2241 ALIGN(off + len, PAGE_CACHE_SIZE) - 1);
2242
2243 /* clone data */ 2254 /* clone data */
2244 key.objectid = btrfs_ino(src); 2255 key.objectid = btrfs_ino(src);
2245 key.type = BTRFS_EXTENT_DATA_KEY; 2256 key.type = BTRFS_EXTENT_DATA_KEY;
@@ -2317,7 +2328,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
2317 else 2328 else
2318 new_key.offset = destoff; 2329 new_key.offset = destoff;
2319 2330
2320 trans = btrfs_start_transaction(root, 1); 2331 /*
2332 * 1 - adjusting old extent (we may have to split it)
2333 * 1 - add new extent
2334 * 1 - inode update
2335 */
2336 trans = btrfs_start_transaction(root, 3);
2321 if (IS_ERR(trans)) { 2337 if (IS_ERR(trans)) {
2322 ret = PTR_ERR(trans); 2338 ret = PTR_ERR(trans);
2323 goto out; 2339 goto out;
@@ -2325,14 +2341,21 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
2325 2341
2326 if (type == BTRFS_FILE_EXTENT_REG || 2342 if (type == BTRFS_FILE_EXTENT_REG ||
2327 type == BTRFS_FILE_EXTENT_PREALLOC) { 2343 type == BTRFS_FILE_EXTENT_PREALLOC) {
2344 /*
2345 * a | --- range to clone ---| b
2346 * | ------------- extent ------------- |
2347 */
2348
2349 /* substract range b */
2350 if (key.offset + datal > off + len)
2351 datal = off + len - key.offset;
2352
2353 /* substract range a */
2328 if (off > key.offset) { 2354 if (off > key.offset) {
2329 datao += off - key.offset; 2355 datao += off - key.offset;
2330 datal -= off - key.offset; 2356 datal -= off - key.offset;
2331 } 2357 }
2332 2358
2333 if (key.offset + datal > off + len)
2334 datal = off + len - key.offset;
2335
2336 ret = btrfs_drop_extents(trans, inode, 2359 ret = btrfs_drop_extents(trans, inode,
2337 new_key.offset, 2360 new_key.offset,
2338 new_key.offset + datal, 2361 new_key.offset + datal,
@@ -2429,7 +2452,6 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
2429 if (endoff > inode->i_size) 2452 if (endoff > inode->i_size)
2430 btrfs_i_size_write(inode, endoff); 2453 btrfs_i_size_write(inode, endoff);
2431 2454
2432 BTRFS_I(inode)->flags = BTRFS_I(src)->flags;
2433 ret = btrfs_update_inode(trans, root, inode); 2455 ret = btrfs_update_inode(trans, root, inode);
2434 BUG_ON(ret); 2456 BUG_ON(ret);
2435 btrfs_end_transaction(trans, root); 2457 btrfs_end_transaction(trans, root);