diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 40 |
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); |