diff options
author | Ashish Samant <ashish.samant@oracle.com> | 2016-09-19 17:44:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-09-19 18:36:17 -0400 |
commit | d21c353d5e99c56cdd5b5c1183ffbcaf23b8b960 (patch) | |
tree | d0c56ebf05ea58999b3e1ec2fae968e7ee94c05b /fs/ocfs2/file.c | |
parent | d979a39d7242e0601bf9b60e89628fb8ac577179 (diff) |
ocfs2: fix start offset to ocfs2_zero_range_for_truncate()
If we punch a hole on a reflink such that following conditions are met:
1. start offset is on a cluster boundary
2. end offset is not on a cluster boundary
3. (end offset is somewhere in another extent) or
(hole range > MAX_CONTIG_BYTES(1MB)),
we dont COW the first cluster starting at the start offset. But in this
case, we were wrongly passing this cluster to
ocfs2_zero_range_for_truncate() to zero out. This will modify the
cluster in place and zero it in the source too.
Fix this by skipping this cluster in such a scenario.
To reproduce:
1. Create a random file of say 10 MB
xfs_io -c 'pwrite -b 4k 0 10M' -f 10MBfile
2. Reflink it
reflink -f 10MBfile reflnktest
3. Punch a hole at starting at cluster boundary with range greater that
1MB. You can also use a range that will put the end offset in another
extent.
fallocate -p -o 0 -l 1048615 reflnktest
4. sync
5. Check the first cluster in the source file. (It will be zeroed out).
dd if=10MBfile iflag=direct bs=<cluster size> count=1 | hexdump -C
Link: http://lkml.kernel.org/r/1470957147-14185-1-git-send-email-ashish.samant@oracle.com
Signed-off-by: Ashish Samant <ashish.samant@oracle.com>
Reported-by: Saar Maoz <saar.maoz@oracle.com>
Reviewed-by: Srinivas Eeda <srinivas.eeda@oracle.com>
Cc: Mark Fasheh <mfasheh@suse.de>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Joseph Qi <joseph.qi@huawei.com>
Cc: Eric Ren <zren@suse.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ocfs2/file.c')
-rw-r--r-- | fs/ocfs2/file.c | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4e7b0dc22450..0b055bfb8e86 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c | |||
@@ -1506,7 +1506,8 @@ static int ocfs2_zero_partial_clusters(struct inode *inode, | |||
1506 | u64 start, u64 len) | 1506 | u64 start, u64 len) |
1507 | { | 1507 | { |
1508 | int ret = 0; | 1508 | int ret = 0; |
1509 | u64 tmpend, end = start + len; | 1509 | u64 tmpend = 0; |
1510 | u64 end = start + len; | ||
1510 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 1511 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); |
1511 | unsigned int csize = osb->s_clustersize; | 1512 | unsigned int csize = osb->s_clustersize; |
1512 | handle_t *handle; | 1513 | handle_t *handle; |
@@ -1538,18 +1539,31 @@ static int ocfs2_zero_partial_clusters(struct inode *inode, | |||
1538 | } | 1539 | } |
1539 | 1540 | ||
1540 | /* | 1541 | /* |
1541 | * We want to get the byte offset of the end of the 1st cluster. | 1542 | * If start is on a cluster boundary and end is somewhere in another |
1543 | * cluster, we have not COWed the cluster starting at start, unless | ||
1544 | * end is also within the same cluster. So, in this case, we skip this | ||
1545 | * first call to ocfs2_zero_range_for_truncate() truncate and move on | ||
1546 | * to the next one. | ||
1542 | */ | 1547 | */ |
1543 | tmpend = (u64)osb->s_clustersize + (start & ~(osb->s_clustersize - 1)); | 1548 | if ((start & (csize - 1)) != 0) { |
1544 | if (tmpend > end) | 1549 | /* |
1545 | tmpend = end; | 1550 | * We want to get the byte offset of the end of the 1st |
1551 | * cluster. | ||
1552 | */ | ||
1553 | tmpend = (u64)osb->s_clustersize + | ||
1554 | (start & ~(osb->s_clustersize - 1)); | ||
1555 | if (tmpend > end) | ||
1556 | tmpend = end; | ||
1546 | 1557 | ||
1547 | trace_ocfs2_zero_partial_clusters_range1((unsigned long long)start, | 1558 | trace_ocfs2_zero_partial_clusters_range1( |
1548 | (unsigned long long)tmpend); | 1559 | (unsigned long long)start, |
1560 | (unsigned long long)tmpend); | ||
1549 | 1561 | ||
1550 | ret = ocfs2_zero_range_for_truncate(inode, handle, start, tmpend); | 1562 | ret = ocfs2_zero_range_for_truncate(inode, handle, start, |
1551 | if (ret) | 1563 | tmpend); |
1552 | mlog_errno(ret); | 1564 | if (ret) |
1565 | mlog_errno(ret); | ||
1566 | } | ||
1553 | 1567 | ||
1554 | if (tmpend < end) { | 1568 | if (tmpend < end) { |
1555 | /* | 1569 | /* |