Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_vnops.c
Show First 20 Lines • Show All 3,267 Lines • ▼ Show 20 Lines | |||||
* It can also be called by a VOP_COPY_FILE_RANGE() to do the work, if there | * It can also be called by a VOP_COPY_FILE_RANGE() to do the work, if there | ||||
* is no better file system specific way to do it. | * is no better file system specific way to do it. | ||||
*/ | */ | ||||
int | int | ||||
vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, | vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, | ||||
struct vnode *outvp, off_t *outoffp, size_t *lenp, unsigned int flags, | struct vnode *outvp, off_t *outoffp, size_t *lenp, unsigned int flags, | ||||
struct ucred *incred, struct ucred *outcred, struct thread *fsize_td) | struct ucred *incred, struct ucred *outcred, struct thread *fsize_td) | ||||
{ | { | ||||
struct vattr inva; | |||||
struct mount *mp; | struct mount *mp; | ||||
off_t startoff, endoff, xfer, xfer2; | off_t startoff, endoff, xfer, xfer2; | ||||
u_long blksize; | u_long blksize; | ||||
int error, interrupted; | int error, interrupted; | ||||
bool cantseek, readzeros, eof, lastblock, holetoeof; | bool cantseek, readzeros, eof, lastblock, holetoeof, sparse; | ||||
ssize_t aresid, r = 0; | ssize_t aresid, r = 0; | ||||
size_t copylen, len, savlen; | size_t copylen, len, savlen; | ||||
off_t insize, outsize; | off_t outsize; | ||||
char *dat; | char *dat; | ||||
long holein, holeout; | long holein, holeout; | ||||
struct timespec curts, endts; | struct timespec curts, endts; | ||||
holein = holeout = 0; | holein = holeout = 0; | ||||
savlen = len = *lenp; | savlen = len = *lenp; | ||||
error = 0; | error = 0; | ||||
interrupted = 0; | interrupted = 0; | ||||
dat = NULL; | dat = NULL; | ||||
error = vn_lock(invp, LK_SHARED); | error = vn_lock(invp, LK_SHARED); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0) | if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0) | ||||
holein = 0; | holein = 0; | ||||
error = vn_getsize_locked(invp, &insize, incred); | error = VOP_GETATTR(invp, &inva, incred); | ||||
if (error == 0 && inva.va_size > OFF_MAX) | |||||
error = EFBIG; | |||||
VOP_UNLOCK(invp); | VOP_UNLOCK(invp); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
/* | |||||
* Use va_bytes >= va_size as a hint that the file does not have | |||||
* sufficient holes to justify the overhead of doing FIOSEEKHOLE. | |||||
* This hint does not work well for file systems doing compression | |||||
kib: I do not think this is true. For instance, for UFS, ext attr blocks are accounted in va_bytes… | |||||
Done Inline ActionsDoes this updated comment look better? rmacklem: Does this updated comment look better?
| |||||
Not Done Inline ActionsYes. kib: Yes. | |||||
* and may fail when allocations for extended attributes increases | |||||
* the value of va_bytes to >= va_size. | |||||
*/ | |||||
sparse = true; | |||||
if (holein != 0 && inva.va_bytes >= inva.va_size) { | |||||
holein = 0; | |||||
sparse = false; | |||||
} | |||||
mp = NULL; | mp = NULL; | ||||
error = vn_start_write(outvp, &mp, V_WAIT); | error = vn_start_write(outvp, &mp, V_WAIT); | ||||
if (error == 0) | if (error == 0) | ||||
error = vn_lock(outvp, LK_EXCLUSIVE); | error = vn_lock(outvp, LK_EXCLUSIVE); | ||||
if (error == 0) { | if (error == 0) { | ||||
/* | /* | ||||
* If fsize_td != NULL, do a vn_rlimit_fsizex() call, | * If fsize_td != NULL, do a vn_rlimit_fsizex() call, | ||||
* now that outvp is locked. | * now that outvp is locked. | ||||
Show All 17 Lines | if (error == 0) { | ||||
* of zero bytes. So, truncate the output file as far as | * of zero bytes. So, truncate the output file as far as | ||||
* possible and then use size to decide if writing 0 | * possible and then use size to decide if writing 0 | ||||
* bytes is necessary in the loop below. | * bytes is necessary in the loop below. | ||||
*/ | */ | ||||
if (error == 0) | if (error == 0) | ||||
error = vn_getsize_locked(outvp, &outsize, outcred); | error = vn_getsize_locked(outvp, &outsize, outcred); | ||||
if (error == 0 && outsize > *outoffp && | if (error == 0 && outsize > *outoffp && | ||||
*outoffp <= OFF_MAX - len && outsize <= *outoffp + len && | *outoffp <= OFF_MAX - len && outsize <= *outoffp + len && | ||||
*inoffp < insize && | *inoffp < inva.va_size && | ||||
*outoffp <= OFF_MAX - (insize - *inoffp) && | *outoffp <= OFF_MAX - (inva.va_size - *inoffp) && | ||||
outsize <= *outoffp + (insize - *inoffp)) { | outsize <= *outoffp + (inva.va_size - *inoffp)) { | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_vnode_check_write(curthread->td_ucred, | error = mac_vnode_check_write(curthread->td_ucred, | ||||
outcred, outvp); | outcred, outvp); | ||||
if (error == 0) | if (error == 0) | ||||
#endif | #endif | ||||
error = vn_truncate_locked(outvp, *outoffp, | error = vn_truncate_locked(outvp, *outoffp, | ||||
false, outcred); | false, outcred); | ||||
if (error == 0) | if (error == 0) | ||||
outsize = *outoffp; | outsize = *outoffp; | ||||
} | } | ||||
VOP_UNLOCK(outvp); | VOP_UNLOCK(outvp); | ||||
} | } | ||||
if (mp != NULL) | if (mp != NULL) | ||||
vn_finished_write(mp); | vn_finished_write(mp); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
if (holein == 0 && holeout > 0) { | if (sparse && holein == 0 && holeout > 0) { | ||||
/* | /* | ||||
* For this special case, the input data will be scanned | * For this special case, the input data will be scanned | ||||
* for blocks of all 0 bytes. For these blocks, the | * for blocks of all 0 bytes. For these blocks, the | ||||
* write can be skipped for the output file to create | * write can be skipped for the output file to create | ||||
* an unallocated region. | * an unallocated region. | ||||
* Therefore, use the appropriate size for the output file. | * Therefore, use the appropriate size for the output file. | ||||
*/ | */ | ||||
blksize = holeout; | blksize = holeout; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | while (len > 0 && error == 0 && !eof && interrupted == 0) { | ||||
* For input files that don't support FIOSEEKDATA/FIOSEEKHOLE, | * For input files that don't support FIOSEEKDATA/FIOSEEKHOLE, | ||||
* the code just falls through to the inner copy loop. | * the code just falls through to the inner copy loop. | ||||
*/ | */ | ||||
error = EINVAL; | error = EINVAL; | ||||
if (holein > 0) { | if (holein > 0) { | ||||
error = VOP_IOCTL(invp, FIOSEEKDATA, &startoff, 0, | error = VOP_IOCTL(invp, FIOSEEKDATA, &startoff, 0, | ||||
incred, curthread); | incred, curthread); | ||||
if (error == ENXIO) { | if (error == ENXIO) { | ||||
startoff = endoff = insize; | startoff = endoff = inva.va_size; | ||||
eof = holetoeof = true; | eof = holetoeof = true; | ||||
error = 0; | error = 0; | ||||
} | } | ||||
} | } | ||||
if (error == 0 && !holetoeof) { | if (error == 0 && !holetoeof) { | ||||
endoff = startoff; | endoff = startoff; | ||||
error = VOP_IOCTL(invp, FIOSEEKHOLE, &endoff, 0, | error = VOP_IOCTL(invp, FIOSEEKHOLE, &endoff, 0, | ||||
incred, curthread); | incred, curthread); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (error == 0) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
copylen = MIN(len, endoff - startoff); | copylen = MIN(len, endoff - startoff); | ||||
cantseek = false; | cantseek = false; | ||||
} else { | } else { | ||||
cantseek = true; | cantseek = true; | ||||
if (!sparse) | |||||
cantseek = false; | |||||
startoff = *inoffp; | startoff = *inoffp; | ||||
copylen = len; | copylen = len; | ||||
error = 0; | error = 0; | ||||
} | } | ||||
xfer = blksize; | xfer = blksize; | ||||
if (cantseek) { | if (cantseek) { | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 641 Lines • Show Last 20 Lines |
I do not think this is true. For instance, for UFS, ext attr blocks are accounted in va_bytes, but not in va_size. So the file might be sparse still.