* need something else than vn_generic_copy_file_range().
*/
- /* Lock both vnodes, avoiding risk of deadlock. */
- do {
- mp = NULL;
- error = vn_start_write(outvp, &mp, V_WAIT);
- if (error == 0) {
- error = vn_lock(outvp, LK_EXCLUSIVE);
- if (error == 0) {
- if (invp == outvp)
- break;
- error = vn_lock(invp, LK_SHARED | LK_NOWAIT);
- if (error == 0)
- break;
- VOP_UNLOCK(outvp);
- if (mp != NULL)
- vn_finished_write(mp);
- mp = NULL;
- error = vn_lock(invp, LK_SHARED);
- if (error == 0)
- VOP_UNLOCK(invp);
- }
+ vn_start_write(outvp, &mp, V_WAIT);
+ if (invp == outvp) {
+ if (vn_lock(outvp, LK_EXCLUSIVE) != 0) {
+ goto bad_write_fallback;
}
- if (mp != NULL)
- vn_finished_write(mp);
- } while (error == 0);
- if (error != 0)
- return (error);
+ } else {
+#if __FreeBSD_version >= 1400086
+ vn_lock_pair(invp, false, LK_EXCLUSIVE, outvp, false,
+ LK_EXCLUSIVE);
+#else
+ vn_lock_pair(invp, false, outvp, false);
+#endif
+ if (VN_IS_DOOMED(invp) || VN_IS_DOOMED(outvp)) {
+ goto bad_locked_fallback;
+ }
+ }
+
#ifdef MAC
error = mac_vnode_check_write(curthread->td_ucred, ap->a_outcred,
outvp);
if (error != 0)
- goto unlock;
+ goto out_locked;
#endif
io.uio_offset = *ap->a_outoffp;
io.uio_resid = *ap->a_lenp;
error = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd);
if (error != 0)
- goto unlock;
+ goto out_locked;
error = zfs_clone_range(VTOZ(invp), ap->a_inoffp, VTOZ(outvp),
- ap->a_outoffp, &len, ap->a_fsizetd->td_ucred);
+ ap->a_outoffp, &len, ap->a_outcred);
+ if (error == EXDEV)
+ goto bad_locked_fallback;
*ap->a_lenp = (size_t)len;
-
-unlock:
+out_locked:
if (invp != outvp)
VOP_UNLOCK(invp);
VOP_UNLOCK(outvp);
if (mp != NULL)
vn_finished_write(mp);
+ return (error);
+bad_locked_fallback:
+ if (invp != outvp)
+ VOP_UNLOCK(invp);
+ VOP_UNLOCK(outvp);
+bad_write_fallback:
+ if (mp != NULL)
+ vn_finished_write(mp);
+ error = vn_generic_copy_file_range(ap->a_invp, ap->a_inoffp,
+ ap->a_outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags,
+ ap->a_incred, ap->a_outcred, ap->a_fsizetd);
return (error);
}