@@ -5,11 +5,19 @@ use std::ffi::OsString;
55use std:: os:: raw;
66use std:: os:: unix:: ffi:: OsStringExt ;
77use std:: os:: unix:: io:: RawFd ;
8+ // For splice and copy_file_range
9+ #[ cfg( any(
10+ target_os = "android" ,
11+ target_os = "freebsd" ,
12+ target_os = "linux"
13+ ) ) ]
14+ use std:: {
15+ os:: unix:: io:: { AsFd , AsRawFd } ,
16+ ptr,
17+ } ;
818
919#[ cfg( feature = "fs" ) ]
1020use crate :: { sys:: stat:: Mode , NixPath , Result } ;
11- #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
12- use std:: ptr; // For splice and copy_file_range
1321
1422#[ cfg( any(
1523 target_os = "linux" ,
@@ -612,44 +620,65 @@ feature! {
612620///
613621/// The `copy_file_range` system call performs an in-kernel copy between
614622/// file descriptors `fd_in` and `fd_out` without the additional cost of
615- /// transferring data from the kernel to user space and then back into the
616- /// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
617- /// file descriptor `fd_out`, overwriting any data that exists within the
618- /// requested range of the target file.
623+ /// transferring data from the kernel to user space and back again. There may be
624+ /// additional optimizations for specific file systems. It copies up to `len`
625+ /// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`,
626+ /// overwriting any data that exists within the requested range of the target
627+ /// file.
619628///
620629/// If the `off_in` and/or `off_out` arguments are used, the values
621630/// will be mutated to reflect the new position within the file after
622- /// copying. If they are not used, the relevant filedescriptors will be seeked
631+ /// copying. If they are not used, the relevant file descriptors will be seeked
623632/// to the new position.
624633///
625634/// On successful completion the number of bytes actually copied will be
626635/// returned.
627- #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
628- pub fn copy_file_range(
629- fd_in: RawFd ,
630- off_in: Option <& mut libc:: loff_t>,
631- fd_out: RawFd ,
632- off_out: Option <& mut libc:: loff_t>,
636+ // Note: FreeBSD defines the offset argument as "off_t". Linux and Android
637+ // define it as "loff_t". But on both OSes, on all supported platforms, those
638+ // are 64 bits. So Nix uses i64 to make the docs simple and consistent.
639+ #[ cfg( any( target_os = "android" , target_os = "freebsd" , target_os = "linux" ) ) ]
640+ pub fn copy_file_range<Fd1 : AsFd , Fd2 : AsFd >(
641+ fd_in: Fd1 ,
642+ off_in: Option <& mut i64 >,
643+ fd_out: Fd2 ,
644+ off_out: Option <& mut i64 >,
633645 len: usize ,
634646) -> Result <usize > {
635647 let off_in = off_in
636- . map( |offset| offset as * mut libc :: loff_t )
648+ . map( |offset| offset as * mut i64 )
637649 . unwrap_or( ptr:: null_mut( ) ) ;
638650 let off_out = off_out
639- . map( |offset| offset as * mut libc :: loff_t )
651+ . map( |offset| offset as * mut i64 )
640652 . unwrap_or( ptr:: null_mut( ) ) ;
641653
642- let ret = unsafe {
643- libc:: syscall(
644- libc:: SYS_copy_file_range ,
645- fd_in,
646- off_in,
647- fd_out,
648- off_out,
649- len,
650- 0 ,
651- )
652- } ;
654+ cfg_if:: cfg_if! {
655+ if #[ cfg( target_os = "freebsd" ) ] {
656+ let ret = unsafe {
657+ libc:: copy_file_range(
658+ fd_in. as_fd( ) . as_raw_fd( ) ,
659+ off_in,
660+ fd_out. as_fd( ) . as_raw_fd( ) ,
661+ off_out,
662+ len,
663+ 0 ,
664+ )
665+ } ;
666+ } else {
667+ // May Linux distros still don't include copy_file_range in their
668+ // libc implementations, so we need to make a direct syscall.
669+ let ret = unsafe {
670+ libc:: syscall(
671+ libc:: SYS_copy_file_range ,
672+ fd_in,
673+ off_in,
674+ fd_out. as_fd( ) . as_raw_fd( ) ,
675+ off_out,
676+ len,
677+ 0 ,
678+ )
679+ } ;
680+ }
681+ }
653682 Errno :: result( ret) . map( |r| r as usize )
654683}
655684
0 commit comments