linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled
@ 2014-05-31  1:16 Filipe David Borba Manana
  2014-05-31 14:12 ` [PATCH v2] " Filipe David Borba Manana
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Filipe David Borba Manana @ 2014-05-31  1:16 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Filipe David Borba Manana

If the NO_HOLES feature is enabled holes don't have file extent items in
the btree that represent them anymore. This made the clone operation
ignore the gaps that exist between consecutive file extent items and
therefore not create the holes at the destination.

A test case for xfstests follows.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---
 fs/btrfs/ioctl.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index ecf56af..bf34b7a 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3009,6 +3009,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
 	int no_quota;
 	u64 len = olen_aligned;
 	u64 last_disko = 0;
+	u64 last_dest_end = (u64)-1;
 
 	ret = -ENOMEM;
 	buf = vmalloc(btrfs_level_size(root, 0));
@@ -3077,6 +3078,7 @@ process_slot:
 			u64 datao = 0, datal = 0;
 			u8 comp;
 			u64 endoff;
+			u64 drop_start;
 
 			extent = btrfs_item_ptr(leaf, slot,
 						struct btrfs_file_extent_item);
@@ -3125,6 +3127,16 @@ process_slot:
 				new_key.offset = destoff;
 
 			/*
+			 * Deal with a hole that doesn't have an extent item
+			 * that represents it (NO_HOLES feature enabled).
+			 */
+			if (last_dest_end != (u64)-1 &&
+			    new_key.offset != last_dest_end)
+				drop_start = last_dest_end;
+			else
+				drop_start = new_key.offset;
+
+			/*
 			 * 1 - adjusting old extent (we may have to split it)
 			 * 1 - add new extent
 			 * 1 - inode update
@@ -3153,7 +3165,7 @@ process_slot:
 				}
 
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 new_key.offset + datal,
 							 1);
 				if (ret) {
@@ -3254,7 +3266,7 @@ process_slot:
 				aligned_end = ALIGN(new_key.offset + datal,
 						    root->sectorsize);
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 aligned_end,
 							 1);
 				if (ret) {
@@ -3301,6 +3313,7 @@ process_slot:
 			 * but shouldn't round up the file size
 			 */
 			endoff = new_key.offset + datal;
+			last_dest_end = endoff;
 			if (endoff > destoff+olen)
 				endoff = destoff+olen;
 			if (endoff > inode->i_size)
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled
  2014-05-31  1:16 [PATCH] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled Filipe David Borba Manana
@ 2014-05-31 14:12 ` Filipe David Borba Manana
  2014-05-31 16:20 ` [PATCH v3] " Filipe David Borba Manana
  2014-06-01  0:50 ` [PATCH v4] " Filipe David Borba Manana
  2 siblings, 0 replies; 5+ messages in thread
From: Filipe David Borba Manana @ 2014-05-31 14:12 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Filipe David Borba Manana

If the NO_HOLES feature is enabled holes don't have file extent items in
the btree that represent them anymore. This made the clone operation
ignore the gaps that exist between consecutive file extent items and
therefore not create the holes at the destination.

A test case for xfstests follows.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---

V2: Deal with holes at the boundaries of the cloning range and that
    either overlap the boundary completely or partially.
    Test case for xfstests updated too to test these 2 cases.

 fs/btrfs/ioctl.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 5 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 04ece8f..4a7a311 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2995,7 +2995,8 @@ out:
  * @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)
+		       const u64 off, const u64 olen, const u64 olen_aligned,
+		       const u64 destoff)
 {
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct btrfs_path *path = NULL;
@@ -3007,8 +3008,10 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
 	int slot;
 	int ret;
 	int no_quota;
-	u64 len = olen_aligned;
+	const u64 len = olen_aligned;
 	u64 last_disko = 0;
+	u64 last_dest_end = destoff;
+	bool add_trailing_hole = false;
 
 	ret = -ENOMEM;
 	buf = vmalloc(btrfs_level_size(root, 0));
@@ -3077,6 +3080,7 @@ process_slot:
 			u64 datao = 0, datal = 0;
 			u8 comp;
 			u64 endoff;
+			u64 drop_start;
 
 			extent = btrfs_item_ptr(leaf, slot,
 						struct btrfs_file_extent_item);
@@ -3106,7 +3110,20 @@ process_slot:
 				path->slots[0]++;
 				goto process_slot;
 			} else if (key.offset >= off + len) {
-				break;
+				if (last_dest_end < destoff + len) {
+					/*
+					 * We have an implicit hole (NO_HOLES
+					 * feature is enabled) that fully or
+					 * partially overlaps our cloning range
+					 * at its end.
+					 */
+					btrfs_release_path(path);
+					path->leave_spinning = 0;
+					add_trailing_hole = true;
+					goto start_trans;
+				} else {
+					break;
+				}
 			}
 
 			size = btrfs_item_size_nr(leaf, slot);
@@ -3125,6 +3142,19 @@ process_slot:
 				new_key.offset = destoff;
 
 			/*
+			 * Deal with a hole that doesn't have an extent item
+			 * that represents it (NO_HOLES feature enabled).
+			 * This hole is either in the middle of the cloning
+			 * range or at the beginning (fully overlaps it or
+			 * partially overlaps it).
+			 */
+			if (new_key.offset != last_dest_end)
+				drop_start = last_dest_end;
+			else
+				drop_start = new_key.offset;
+
+start_trans:
+			/*
 			 * 1 - adjusting old extent (we may have to split it)
 			 * 1 - add new extent
 			 * 1 - inode update
@@ -3135,6 +3165,19 @@ process_slot:
 				goto out;
 			}
 
+			if (add_trailing_hole) {
+				ret = btrfs_drop_extents(trans, root, inode,
+							 last_dest_end,
+							 destoff + len, 1);
+				if (ret && ret != -EOPNOTSUPP)
+					btrfs_abort_transaction(trans, root,
+								ret);
+				btrfs_end_transaction(trans, root);
+				if (ret)
+					goto out;
+				break;
+			}
+
 			if (type == BTRFS_FILE_EXTENT_REG ||
 			    type == BTRFS_FILE_EXTENT_PREALLOC) {
 				/*
@@ -3153,7 +3196,7 @@ process_slot:
 				}
 
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 new_key.offset + datal,
 							 1);
 				if (ret) {
@@ -3254,7 +3297,7 @@ process_slot:
 				aligned_end = ALIGN(new_key.offset + datal,
 						    root->sectorsize);
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 aligned_end,
 							 1);
 				if (ret) {
@@ -3301,6 +3344,7 @@ process_slot:
 			 * but shouldn't round up the file size
 			 */
 			endoff = new_key.offset + datal;
+			last_dest_end = endoff;
 			if (endoff > destoff+olen)
 				endoff = destoff+olen;
 			if (endoff > inode->i_size)
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled
  2014-05-31  1:16 [PATCH] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled Filipe David Borba Manana
  2014-05-31 14:12 ` [PATCH v2] " Filipe David Borba Manana
@ 2014-05-31 16:20 ` Filipe David Borba Manana
  2014-06-01  0:50 ` [PATCH v4] " Filipe David Borba Manana
  2 siblings, 0 replies; 5+ messages in thread
From: Filipe David Borba Manana @ 2014-05-31 16:20 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Filipe David Borba Manana

If the NO_HOLES feature is enabled holes don't have file extent items in
the btree that represent them anymore. This made the clone operation
ignore the gaps that exist between consecutive file extent items and
therefore not create the holes at the destination.

A test case for xfstests follows.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---

V2: Deal with holes at the boundaries of the cloning range and that
    either overlap the boundary completely or partially.
    Test case for xfstests updated too to test these 2 cases.

V3: Deal with the case where the cloning range overlaps (partially or
    completely) a hole at the end of the source file, and might increase
    the size of the target file.
    Updated the test for xfstests to cover these cases too.

 fs/btrfs/ioctl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 59 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 04ece8f..f508f5e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2995,7 +2995,8 @@ out:
  * @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)
+		       const u64 off, const u64 olen, const u64 olen_aligned,
+		       const u64 destoff)
 {
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct btrfs_path *path = NULL;
@@ -3007,8 +3008,9 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
 	int slot;
 	int ret;
 	int no_quota;
-	u64 len = olen_aligned;
+	const u64 len = olen_aligned;
 	u64 last_disko = 0;
+	u64 last_dest_end = destoff;
 
 	ret = -ENOMEM;
 	buf = vmalloc(btrfs_level_size(root, 0));
@@ -3077,6 +3079,7 @@ process_slot:
 			u64 datao = 0, datal = 0;
 			u8 comp;
 			u64 endoff;
+			u64 drop_start;
 
 			extent = btrfs_item_ptr(leaf, slot,
 						struct btrfs_file_extent_item);
@@ -3125,6 +3128,18 @@ process_slot:
 				new_key.offset = destoff;
 
 			/*
+			 * Deal with a hole that doesn't have an extent item
+			 * that represents it (NO_HOLES feature enabled).
+			 * This hole is either in the middle of the cloning
+			 * range or at the beginning (fully overlaps it or
+			 * partially overlaps it).
+			 */
+			if (new_key.offset != last_dest_end)
+				drop_start = last_dest_end;
+			else
+				drop_start = new_key.offset;
+
+			/*
 			 * 1 - adjusting old extent (we may have to split it)
 			 * 1 - add new extent
 			 * 1 - inode update
@@ -3153,7 +3168,7 @@ process_slot:
 				}
 
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 new_key.offset + datal,
 							 1);
 				if (ret) {
@@ -3254,7 +3269,7 @@ process_slot:
 				aligned_end = ALIGN(new_key.offset + datal,
 						    root->sectorsize);
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 aligned_end,
 							 1);
 				if (ret) {
@@ -3301,6 +3316,7 @@ process_slot:
 			 * but shouldn't round up the file size
 			 */
 			endoff = new_key.offset + datal;
+			last_dest_end = endoff;
 			if (endoff > destoff+olen)
 				endoff = destoff+olen;
 			if (endoff > inode->i_size)
@@ -3321,6 +3337,45 @@ process_slot:
 	}
 	ret = 0;
 
+	if (last_dest_end < destoff + len) {
+		/*
+		 * We have an implicit hole (NO_HOLES feature is enabled) that
+		 * fully or partially overlaps our cloning range at its end.
+		 */
+		btrfs_release_path(path);
+		path->leave_spinning = 0;
+
+		/*
+		 * 1 - remove extent(s)
+		 * 1 - inode update
+		 */
+		trans = btrfs_start_transaction(root, 2);
+		if (IS_ERR(trans)) {
+			ret = PTR_ERR(trans);
+			goto out;
+		}
+		ret = btrfs_drop_extents(trans, root, inode,
+					 last_dest_end, destoff + len, 1);
+		if (ret) {
+			if (ret != -EOPNOTSUPP)
+				btrfs_abort_transaction(trans, root, ret);
+			btrfs_end_transaction(trans, root);
+			goto out;
+		}
+		last_dest_end = destoff + len;
+		if (last_dest_end > destoff + olen)
+			last_dest_end = destoff + olen;
+		if (last_dest_end > inode->i_size)
+			btrfs_i_size_write(inode, last_dest_end);
+		ret = btrfs_update_inode(trans, root, inode);
+		if (ret) {
+			btrfs_abort_transaction(trans, root, ret);
+			btrfs_end_transaction(trans, root);
+			goto out;
+		}
+		ret = btrfs_end_transaction(trans, root);
+	}
+
 out:
 	btrfs_free_path(path);
 	vfree(buf);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v4] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled
  2014-05-31  1:16 [PATCH] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled Filipe David Borba Manana
  2014-05-31 14:12 ` [PATCH v2] " Filipe David Borba Manana
  2014-05-31 16:20 ` [PATCH v3] " Filipe David Borba Manana
@ 2014-06-01  0:50 ` Filipe David Borba Manana
  2014-06-04  8:25   ` Liu Bo
  2 siblings, 1 reply; 5+ messages in thread
From: Filipe David Borba Manana @ 2014-06-01  0:50 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Filipe David Borba Manana

If the NO_HOLES feature is enabled holes don't have file extent items in
the btree that represent them anymore. This made the clone operation
ignore the gaps that exist between consecutive file extent items and
therefore not create the holes at the destination. When not using the
NO_HOLES feature, the holes were created at the destination.

A test case for xfstests follows.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
---

V2: Deal with holes at the boundaries of the cloning range and that
    either overlap the boundary completely or partially.
    Test case for xfstests updated too to test these 2 cases.

V3: Deal with the case where the cloning range overlaps (partially or
    completely) a hole at the end of the source file, and might increase
    the size of the target file.
    Updated the test for xfstests to cover these cases too.

V4: Moved some duplicated code into an helper function.

 fs/btrfs/ioctl.c | 108 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 83 insertions(+), 25 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 04ece8f..95194a9 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2983,6 +2983,37 @@ out:
 	return ret;
 }
 
+static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
+				     struct inode *inode,
+				     u64 endoff,
+				     const u64 destoff,
+				     const u64 olen)
+{
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	int ret;
+
+	inode_inc_iversion(inode);
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	/*
+	 * We round up to the block size at eof when determining which
+	 * extents to clone above, but shouldn't round up the file size.
+	 */
+	if (endoff > destoff + olen)
+		endoff = destoff + olen;
+	if (endoff > inode->i_size)
+		btrfs_i_size_write(inode, endoff);
+
+	ret = btrfs_update_inode(trans, root, inode);
+	if (ret) {
+		btrfs_abort_transaction(trans, root, ret);
+		btrfs_end_transaction(trans, root);
+		goto out;
+	}
+	ret = btrfs_end_transaction(trans, root);
+out:
+	return ret;
+}
+
 /**
  * btrfs_clone() - clone a range from inode file to another
  *
@@ -2995,7 +3026,8 @@ out:
  * @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)
+		       const u64 off, const u64 olen, const u64 olen_aligned,
+		       const u64 destoff)
 {
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct btrfs_path *path = NULL;
@@ -3007,8 +3039,9 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
 	int slot;
 	int ret;
 	int no_quota;
-	u64 len = olen_aligned;
+	const u64 len = olen_aligned;
 	u64 last_disko = 0;
+	u64 last_dest_end = destoff;
 
 	ret = -ENOMEM;
 	buf = vmalloc(btrfs_level_size(root, 0));
@@ -3076,7 +3109,7 @@ process_slot:
 			u64 disko = 0, diskl = 0;
 			u64 datao = 0, datal = 0;
 			u8 comp;
-			u64 endoff;
+			u64 drop_start;
 
 			extent = btrfs_item_ptr(leaf, slot,
 						struct btrfs_file_extent_item);
@@ -3125,6 +3158,18 @@ process_slot:
 				new_key.offset = destoff;
 
 			/*
+			 * Deal with a hole that doesn't have an extent item
+			 * that represents it (NO_HOLES feature enabled).
+			 * This hole is either in the middle of the cloning
+			 * range or at the beginning (fully overlaps it or
+			 * partially overlaps it).
+			 */
+			if (new_key.offset != last_dest_end)
+				drop_start = last_dest_end;
+			else
+				drop_start = new_key.offset;
+
+			/*
 			 * 1 - adjusting old extent (we may have to split it)
 			 * 1 - add new extent
 			 * 1 - inode update
@@ -3153,7 +3198,7 @@ process_slot:
 				}
 
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 new_key.offset + datal,
 							 1);
 				if (ret) {
@@ -3254,7 +3299,7 @@ process_slot:
 				aligned_end = ALIGN(new_key.offset + datal,
 						    root->sectorsize);
 				ret = btrfs_drop_extents(trans, root, inode,
-							 new_key.offset,
+							 drop_start,
 							 aligned_end,
 							 1);
 				if (ret) {
@@ -3292,27 +3337,12 @@ process_slot:
 			btrfs_mark_buffer_dirty(leaf);
 			btrfs_release_path(path);
 
-			inode_inc_iversion(inode);
-			inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-
-			/*
-			 * we round up to the block size at eof when
-			 * determining which extents to clone above,
-			 * but shouldn't round up the file size
-			 */
-			endoff = new_key.offset + datal;
-			if (endoff > destoff+olen)
-				endoff = destoff+olen;
-			if (endoff > inode->i_size)
-				btrfs_i_size_write(inode, endoff);
-
-			ret = btrfs_update_inode(trans, root, inode);
-			if (ret) {
-				btrfs_abort_transaction(trans, root, ret);
-				btrfs_end_transaction(trans, root);
+			last_dest_end = new_key.offset + datal;
+			ret = clone_finish_inode_update(trans, inode,
+							last_dest_end,
+							destoff, olen);
+			if (ret)
 				goto out;
-			}
-			ret = btrfs_end_transaction(trans, root);
 			if (new_key.offset + datal >= destoff + len)
 				break;
 		}
@@ -3321,6 +3351,34 @@ process_slot:
 	}
 	ret = 0;
 
+	if (last_dest_end < destoff + len) {
+		/*
+		 * We have an implicit hole (NO_HOLES feature is enabled) that
+		 * fully or partially overlaps our cloning range at its end.
+		 */
+		btrfs_release_path(path);
+
+		/*
+		 * 1 - remove extent(s)
+		 * 1 - inode update
+		 */
+		trans = btrfs_start_transaction(root, 2);
+		if (IS_ERR(trans)) {
+			ret = PTR_ERR(trans);
+			goto out;
+		}
+		ret = btrfs_drop_extents(trans, root, inode,
+					 last_dest_end, destoff + len, 1);
+		if (ret) {
+			if (ret != -EOPNOTSUPP)
+				btrfs_abort_transaction(trans, root, ret);
+			btrfs_end_transaction(trans, root);
+			goto out;
+		}
+		ret = clone_finish_inode_update(trans, inode, destoff + len,
+						destoff, olen);
+	}
+
 out:
 	btrfs_free_path(path);
 	vfree(buf);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v4] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled
  2014-06-01  0:50 ` [PATCH v4] " Filipe David Borba Manana
@ 2014-06-04  8:25   ` Liu Bo
  0 siblings, 0 replies; 5+ messages in thread
From: Liu Bo @ 2014-06-04  8:25 UTC (permalink / raw)
  To: Filipe David Borba Manana; +Cc: linux-btrfs

On Sun, Jun 01, 2014 at 01:50:28AM +0100, Filipe David Borba Manana wrote:
> If the NO_HOLES feature is enabled holes don't have file extent items in
> the btree that represent them anymore. This made the clone operation
> ignore the gaps that exist between consecutive file extent items and
> therefore not create the holes at the destination. When not using the
> NO_HOLES feature, the holes were created at the destination.
> 
> A test case for xfstests follows.

Reviewed-by: Liu Bo <bo.li.liu@oracle.com>

-liubo

> 
> Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
> ---
> 
> V2: Deal with holes at the boundaries of the cloning range and that
>     either overlap the boundary completely or partially.
>     Test case for xfstests updated too to test these 2 cases.
> 
> V3: Deal with the case where the cloning range overlaps (partially or
>     completely) a hole at the end of the source file, and might increase
>     the size of the target file.
>     Updated the test for xfstests to cover these cases too.
> 
> V4: Moved some duplicated code into an helper function.
> 
>  fs/btrfs/ioctl.c | 108 ++++++++++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 83 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 04ece8f..95194a9 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -2983,6 +2983,37 @@ out:
>  	return ret;
>  }
>  
> +static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
> +				     struct inode *inode,
> +				     u64 endoff,
> +				     const u64 destoff,
> +				     const u64 olen)
> +{
> +	struct btrfs_root *root = BTRFS_I(inode)->root;
> +	int ret;
> +
> +	inode_inc_iversion(inode);
> +	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
> +	/*
> +	 * We round up to the block size at eof when determining which
> +	 * extents to clone above, but shouldn't round up the file size.
> +	 */
> +	if (endoff > destoff + olen)
> +		endoff = destoff + olen;
> +	if (endoff > inode->i_size)
> +		btrfs_i_size_write(inode, endoff);
> +
> +	ret = btrfs_update_inode(trans, root, inode);
> +	if (ret) {
> +		btrfs_abort_transaction(trans, root, ret);
> +		btrfs_end_transaction(trans, root);
> +		goto out;
> +	}
> +	ret = btrfs_end_transaction(trans, root);
> +out:
> +	return ret;
> +}
> +
>  /**
>   * btrfs_clone() - clone a range from inode file to another
>   *
> @@ -2995,7 +3026,8 @@ out:
>   * @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)
> +		       const u64 off, const u64 olen, const u64 olen_aligned,
> +		       const u64 destoff)
>  {
>  	struct btrfs_root *root = BTRFS_I(inode)->root;
>  	struct btrfs_path *path = NULL;
> @@ -3007,8 +3039,9 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
>  	int slot;
>  	int ret;
>  	int no_quota;
> -	u64 len = olen_aligned;
> +	const u64 len = olen_aligned;
>  	u64 last_disko = 0;
> +	u64 last_dest_end = destoff;
>  
>  	ret = -ENOMEM;
>  	buf = vmalloc(btrfs_level_size(root, 0));
> @@ -3076,7 +3109,7 @@ process_slot:
>  			u64 disko = 0, diskl = 0;
>  			u64 datao = 0, datal = 0;
>  			u8 comp;
> -			u64 endoff;
> +			u64 drop_start;
>  
>  			extent = btrfs_item_ptr(leaf, slot,
>  						struct btrfs_file_extent_item);
> @@ -3125,6 +3158,18 @@ process_slot:
>  				new_key.offset = destoff;
>  
>  			/*
> +			 * Deal with a hole that doesn't have an extent item
> +			 * that represents it (NO_HOLES feature enabled).
> +			 * This hole is either in the middle of the cloning
> +			 * range or at the beginning (fully overlaps it or
> +			 * partially overlaps it).
> +			 */
> +			if (new_key.offset != last_dest_end)
> +				drop_start = last_dest_end;
> +			else
> +				drop_start = new_key.offset;
> +
> +			/*
>  			 * 1 - adjusting old extent (we may have to split it)
>  			 * 1 - add new extent
>  			 * 1 - inode update
> @@ -3153,7 +3198,7 @@ process_slot:
>  				}
>  
>  				ret = btrfs_drop_extents(trans, root, inode,
> -							 new_key.offset,
> +							 drop_start,
>  							 new_key.offset + datal,
>  							 1);
>  				if (ret) {
> @@ -3254,7 +3299,7 @@ process_slot:
>  				aligned_end = ALIGN(new_key.offset + datal,
>  						    root->sectorsize);
>  				ret = btrfs_drop_extents(trans, root, inode,
> -							 new_key.offset,
> +							 drop_start,
>  							 aligned_end,
>  							 1);
>  				if (ret) {
> @@ -3292,27 +3337,12 @@ process_slot:
>  			btrfs_mark_buffer_dirty(leaf);
>  			btrfs_release_path(path);
>  
> -			inode_inc_iversion(inode);
> -			inode->i_mtime = inode->i_ctime = CURRENT_TIME;
> -
> -			/*
> -			 * we round up to the block size at eof when
> -			 * determining which extents to clone above,
> -			 * but shouldn't round up the file size
> -			 */
> -			endoff = new_key.offset + datal;
> -			if (endoff > destoff+olen)
> -				endoff = destoff+olen;
> -			if (endoff > inode->i_size)
> -				btrfs_i_size_write(inode, endoff);
> -
> -			ret = btrfs_update_inode(trans, root, inode);
> -			if (ret) {
> -				btrfs_abort_transaction(trans, root, ret);
> -				btrfs_end_transaction(trans, root);
> +			last_dest_end = new_key.offset + datal;
> +			ret = clone_finish_inode_update(trans, inode,
> +							last_dest_end,
> +							destoff, olen);
> +			if (ret)
>  				goto out;
> -			}
> -			ret = btrfs_end_transaction(trans, root);
>  			if (new_key.offset + datal >= destoff + len)
>  				break;
>  		}
> @@ -3321,6 +3351,34 @@ process_slot:
>  	}
>  	ret = 0;
>  
> +	if (last_dest_end < destoff + len) {
> +		/*
> +		 * We have an implicit hole (NO_HOLES feature is enabled) that
> +		 * fully or partially overlaps our cloning range at its end.
> +		 */
> +		btrfs_release_path(path);
> +
> +		/*
> +		 * 1 - remove extent(s)
> +		 * 1 - inode update
> +		 */
> +		trans = btrfs_start_transaction(root, 2);
> +		if (IS_ERR(trans)) {
> +			ret = PTR_ERR(trans);
> +			goto out;
> +		}
> +		ret = btrfs_drop_extents(trans, root, inode,
> +					 last_dest_end, destoff + len, 1);
> +		if (ret) {
> +			if (ret != -EOPNOTSUPP)
> +				btrfs_abort_transaction(trans, root, ret);
> +			btrfs_end_transaction(trans, root);
> +			goto out;
> +		}
> +		ret = clone_finish_inode_update(trans, inode, destoff + len,
> +						destoff, olen);
> +	}
> +
>  out:
>  	btrfs_free_path(path);
>  	vfree(buf);
> -- 
> 1.9.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-06-04  8:25 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-31  1:16 [PATCH] Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled Filipe David Borba Manana
2014-05-31 14:12 ` [PATCH v2] " Filipe David Borba Manana
2014-05-31 16:20 ` [PATCH v3] " Filipe David Borba Manana
2014-06-01  0:50 ` [PATCH v4] " Filipe David Borba Manana
2014-06-04  8:25   ` Liu Bo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).