@@ -5,11 +5,19 @@ use std::ffi::OsString;
5
5
use std:: os:: raw;
6
6
use std:: os:: unix:: ffi:: OsStringExt ;
7
7
use 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
+ } ;
8
18
9
19
#[ cfg( feature = "fs" ) ]
10
20
use 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
13
21
14
22
#[ cfg( any(
15
23
target_os = "linux" ,
@@ -612,44 +620,65 @@ feature! {
612
620
///
613
621
/// The `copy_file_range` system call performs an in-kernel copy between
614
622
/// 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.
619
628
///
620
629
/// If the `off_in` and/or `off_out` arguments are used, the values
621
630
/// 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
623
632
/// to the new position.
624
633
///
625
634
/// On successful completion the number of bytes actually copied will be
626
635
/// 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 >,
633
645
len: usize ,
634
646
) -> Result <usize > {
635
647
let off_in = off_in
636
- . map( |offset| offset as * mut libc :: loff_t )
648
+ . map( |offset| offset as * mut i64 )
637
649
. unwrap_or( ptr:: null_mut( ) ) ;
638
650
let off_out = off_out
639
- . map( |offset| offset as * mut libc :: loff_t )
651
+ . map( |offset| offset as * mut i64 )
640
652
. unwrap_or( ptr:: null_mut( ) ) ;
641
653
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
+ }
653
682
Errno :: result( ret) . map( |r| r as usize )
654
683
}
655
684
0 commit comments