Btrfs: fix CLONE ioctl destination file size expansion to block boundary
authorSage Weil <sage@newdream.net>
Sat, 12 Jun 2010 22:31:14 +0000 (22:31 +0000)
committerChris Mason <chris.mason@oracle.com>
Mon, 19 Jul 2010 20:15:06 +0000 (16:15 -0400)
The CLONE and CLONE_RANGE ioctls round up the range of extents being
cloned to the block size when the range to clone extends to the end of file
(this is always the case with CLONE).  It was then using that offset when
extending the destination file's i_size.  Fix this by not setting i_size
beyond the originally requested ending offset.

This bug was introduced by a22285a6 (2.6.35-rc1).

Signed-off-by: Sage Weil <sage@newdream.net>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ioctl.c

index 4dbaf89b1337632ac7800b84b183d6cd0ab14242..2a8b3a7568ad9e057e95a40ebdb75db2730ef298 100644 (file)
@@ -1578,6 +1578,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                        u64 disko = 0, diskl = 0;
                        u64 datao = 0, datal = 0;
                        u8 comp;
+                       u64 endoff;
 
                        size = btrfs_item_size_nr(leaf, slot);
                        read_extent_buffer(leaf, buf,
@@ -1712,9 +1713,18 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                        btrfs_release_path(root, path);
 
                        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-                       if (new_key.offset + datal > inode->i_size)
-                               btrfs_i_size_write(inode,
-                                                  new_key.offset + datal);
+
+                       /*
+                        * 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 > off+olen)
+                               endoff = off+olen;
+                       if (endoff > inode->i_size)
+                               btrfs_i_size_write(inode, endoff);
+
                        BTRFS_I(inode)->flags = BTRFS_I(src)->flags;
                        ret = btrfs_update_inode(trans, root, inode);
                        BUG_ON(ret);