44// file that was distributed with this source code.
55// spell-checker:ignore ficlone reflink ftruncate pwrite fiemap lseek
66
7- use libc :: { SEEK_DATA , SEEK_HOLE } ;
7+ use rustix :: fs :: { SeekFrom , ftruncate , ioctl_ficlone , seek } ;
88use std:: fs:: { File , OpenOptions } ;
99use std:: io:: Read ;
1010use std:: os:: unix:: fs:: FileExt ;
1111use std:: os:: unix:: fs:: MetadataExt ;
1212use std:: os:: unix:: fs:: { FileTypeExt , OpenOptionsExt } ;
13- use std:: os:: unix:: io:: AsRawFd ;
1413use std:: path:: Path ;
1514use uucore:: buf_copy;
1615use uucore:: mode:: get_umask;
@@ -60,18 +59,15 @@ where
6059{
6160 let src_file = File :: open ( & source) ?;
6261 let dst_file = File :: create ( & dest) ?;
63- let src_fd = src_file. as_raw_fd ( ) ;
64- let dst_fd = dst_file. as_raw_fd ( ) ;
65- let result = unsafe { libc:: ioctl ( dst_fd, libc:: FICLONE , src_fd) } ;
66- if result == 0 {
67- return Ok ( ( ) ) ;
68- }
69- match fallback {
70- CloneFallback :: Error => Err ( std:: io:: Error :: last_os_error ( ) ) ,
71- CloneFallback :: FSCopy => std:: fs:: copy ( source, dest) . map ( |_| ( ) ) ,
72- CloneFallback :: SparseCopy => sparse_copy ( source, dest) ,
73- CloneFallback :: SparseCopyWithoutHole => sparse_copy_without_hole ( source, dest) ,
62+ if ioctl_ficlone ( dst_file, src_file) . is_err ( ) {
63+ return match fallback {
64+ CloneFallback :: Error => Err ( std:: io:: Error :: last_os_error ( ) ) ,
65+ CloneFallback :: FSCopy => std:: fs:: copy ( source, dest) . map ( |_| ( ) ) ,
66+ CloneFallback :: SparseCopy => sparse_copy ( source, dest) ,
67+ CloneFallback :: SparseCopyWithoutHole => sparse_copy_without_hole ( source, dest) ,
68+ } ;
7469 }
70+ Ok ( ( ) )
7571}
7672
7773/// Checks whether a file contains any non null bytes i.e. any byte != 0x0
@@ -90,16 +86,9 @@ fn check_for_data(source: &Path) -> Result<(bool, u64, u64), std::io::Error> {
9086 let _ = src_file. read ( & mut buf) ?;
9187 return Ok ( ( buf. iter ( ) . any ( |& x| x != 0x0 ) , size, 0 ) ) ;
9288 }
89+ let has_data = seek ( src_file, SeekFrom :: Data ( 0 ) ) . is_ok ( ) ;
9390
94- let src_fd = src_file. as_raw_fd ( ) ;
95-
96- let result = unsafe { libc:: lseek ( src_fd, 0 , SEEK_DATA ) } ;
97-
98- match result {
99- -1 => Ok ( ( false , size, blocks) ) , // No data found or end of file
100- _ if result >= 0 => Ok ( ( true , size, blocks) ) , // Data found
101- _ => Err ( std:: io:: Error :: last_os_error ( ) ) ,
102- }
91+ Ok ( ( has_data, size, blocks) )
10392}
10493
10594#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
@@ -126,42 +115,27 @@ where
126115{
127116 let src_file = File :: open ( source) ?;
128117 let dst_file = File :: create ( dest) ?;
129- let dst_fd = dst_file. as_raw_fd ( ) ;
130118
131119 let size = src_file. metadata ( ) ?. size ( ) ;
132- if unsafe { libc:: ftruncate ( dst_fd, size. try_into ( ) . unwrap ( ) ) } < 0 {
133- return Err ( std:: io:: Error :: last_os_error ( ) ) ;
134- }
135- let src_fd = src_file. as_raw_fd ( ) ;
136- let mut current_offset: isize = 0 ;
120+ ftruncate ( & dst_file, size) ?;
121+ let mut current_offset = 0 ;
137122 // Maximize the data read at once to 16 MiB to avoid memory hogging with large files
138123 // 16 MiB chunks should saturate an SSD
139124 let step = std:: cmp:: min ( size, 16 * 1024 * 1024 ) as usize ;
140125 let mut buf: Vec < u8 > = vec ! [ 0x0 ; step] ;
141- loop {
142- let result = unsafe { libc:: lseek ( src_fd, current_offset. try_into ( ) . unwrap ( ) , SEEK_DATA ) }
143- . try_into ( )
144- . unwrap ( ) ;
145-
146- current_offset = result;
147- let hole: isize =
148- unsafe { libc:: lseek ( src_fd, current_offset. try_into ( ) . unwrap ( ) , SEEK_HOLE ) }
149- . try_into ( )
150- . unwrap ( ) ;
151- if result == -1 || hole == -1 {
126+ while let Ok ( data) = seek ( & src_file, SeekFrom :: Data ( current_offset) ) {
127+ current_offset = data;
128+ let Ok ( hole) = seek ( & src_file, SeekFrom :: Hole ( current_offset) ) else {
152129 break ;
153- }
154- if result <= -2 || hole <= -2 {
155- return Err ( std:: io:: Error :: last_os_error ( ) ) ;
156- }
157- let len: isize = hole - current_offset;
130+ } ;
131+ let len = hole - current_offset;
158132 // Read and write data in chunks of `step` while reusing the same buffer
159133 for i in ( 0 ..len) . step_by ( step) {
160134 // Ensure we don't read past the end of the file or the start of the next hole
161135 let read_len = std:: cmp:: min ( ( len - i) as usize , step) ;
162136 let buf = & mut buf[ ..read_len] ;
163- src_file. read_exact_at ( buf, ( current_offset + i) as u64 ) ?;
164- dst_file. write_all_at ( buf, ( current_offset + i) as u64 ) ?;
137+ src_file. read_exact_at ( buf, current_offset + i) ?;
138+ dst_file. write_all_at ( buf, current_offset + i) ?;
165139 }
166140 current_offset = hole;
167141 }
@@ -176,12 +150,9 @@ where
176150{
177151 let mut src_file = File :: open ( source) ?;
178152 let dst_file = File :: create ( dest) ?;
179- let dst_fd = dst_file. as_raw_fd ( ) ;
180153
181154 let size: usize = src_file. metadata ( ) ?. size ( ) . try_into ( ) . unwrap ( ) ;
182- if unsafe { libc:: ftruncate ( dst_fd, size. try_into ( ) . unwrap ( ) ) } < 0 {
183- return Err ( std:: io:: Error :: last_os_error ( ) ) ;
184- }
155+ ftruncate ( & dst_file, size. try_into ( ) . unwrap ( ) ) ?;
185156
186157 let blksize = dst_file. metadata ( ) ?. blksize ( ) ;
187158 let mut buf: Vec < u8 > = vec ! [ 0 ; blksize. try_into( ) . unwrap( ) ] ;
0 commit comments