aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2020-10-01 00:33:44 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2020-10-01 00:33:44 +0000
commit961afe3c9900af804e0b6ce3fb38a76f240b2511 (patch)
treeea526dc1e7ff78c0fa245e067e94a23edced406f
parent96128185f6022ebbcfb9e2fbfcb55e7b38afad69 (diff)
downloadsrc-961afe3c9900af804e0b6ce3fb38a76f240b2511.tar.gz
src-961afe3c9900af804e0b6ce3fb38a76f240b2511.zip
Clip the "len" argument to vn_generic_copy_file_range() at a
hole size boundary. By clipping the len argument of vn_generic_copy_file_range() to end at an exact multiple of hole size, holes are more likely to be maintained during the copy. A hole can still straddle the boundary at the end of the copy range, resulting in a block being allocated in the output file as it is being grown in size, but this will reduce the likelyhood of this happening. While here, also modify setting of blksize to better handle the case where _PC_MIN_HOLE_SIZE is returned as 1. Reviewed by: asomers Differential Revision: https://reviews.freebsd.org/D26570
Notes
Notes: svn path=/head/; revision=366302
-rw-r--r--sys/kern/vfs_vnops.c14
1 files changed, 12 insertions, 2 deletions
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index e31e62899878..33b05a6c00cd 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -3020,7 +3020,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
int error;
bool cantseek, readzeros, eof, lastblock;
ssize_t aresid;
- size_t copylen, len, savlen;
+ size_t copylen, len, rem, savlen;
char *dat;
long holein, holeout;
@@ -3089,7 +3089,17 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
* This value is clipped at 4Kbytes and 1Mbyte.
*/
blksize = MAX(holein, holeout);
- if (blksize == 0)
+
+ /* Clip len to end at an exact multiple of hole size. */
+ if (blksize > 1) {
+ rem = *inoffp % blksize;
+ if (rem > 0)
+ rem = blksize - rem;
+ if (len - rem > blksize)
+ len = savlen = rounddown(len - rem, blksize) + rem;
+ }
+
+ if (blksize <= 1)
blksize = MAX(invp->v_mount->mnt_stat.f_iosize,
outvp->v_mount->mnt_stat.f_iosize);
if (blksize < 4096)