@@ -1425,6 +1425,47 @@ impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
14251425 Ok ( res as u64 )
14261426 }
14271427 }
1428+
1429+ fn copy_file_range (
1430+ & self ,
1431+ _ctx : & Context ,
1432+ inode_in : Inode ,
1433+ handle_in : Handle ,
1434+ offset_in : u64 ,
1435+ inode_out : Inode ,
1436+ handle_out : Handle ,
1437+ offset_out : u64 ,
1438+ len : u64 ,
1439+ flags : u64 ,
1440+ ) -> io:: Result < usize > {
1441+ // Get file descriptors from handles
1442+ let data_in = self . handle_map . get ( handle_in, inode_in) ?;
1443+ let data_out = self . handle_map . get ( handle_out, inode_out) ?;
1444+
1445+ let ( _guard_in, file_in) = data_in. get_file_mut ( ) ;
1446+ let ( _guard_out, file_out) = data_out. get_file_mut ( ) ;
1447+
1448+ let mut off_in = offset_in as libc:: off64_t ;
1449+ let mut off_out = offset_out as libc:: off64_t ;
1450+
1451+ // Safe because we check the return value and the fds are valid
1452+ let result = unsafe {
1453+ libc:: copy_file_range (
1454+ file_in. as_raw_fd ( ) ,
1455+ & mut off_in,
1456+ file_out. as_raw_fd ( ) ,
1457+ & mut off_out,
1458+ len as libc:: size_t ,
1459+ flags as libc:: c_uint ,
1460+ )
1461+ } ;
1462+
1463+ if result < 0 {
1464+ Err ( io:: Error :: last_os_error ( ) )
1465+ } else {
1466+ Ok ( result as usize )
1467+ }
1468+ }
14281469}
14291470
14301471#[ cfg( test) ]
@@ -1743,4 +1784,78 @@ mod tests {
17431784
17441785 assert ! ( fs. fsyncdir( & ctx, ROOT_ID , false , 0 ) . is_ok( ) ) ;
17451786 }
1787+
1788+ #[ test]
1789+ fn test_copy_file_range ( ) {
1790+ let ( fs, source) = prepare_fs_tmpdir ( ) ;
1791+ let ctx = prepare_context ( ) ;
1792+
1793+ // Create source file with data (using std::fs for simplicity)
1794+ let test_data = b"Hello, copy_file_range!" ;
1795+ let src_path = source. as_path ( ) . join ( "source.txt" ) ;
1796+ let dst_path = source. as_path ( ) . join ( "dest.txt" ) ;
1797+ std:: fs:: write ( & src_path, test_data) . unwrap ( ) ;
1798+ std:: fs:: write ( & dst_path, b"" ) . unwrap ( ) ; // Create empty destination
1799+
1800+ // Look up and open both files through the passthrough fs
1801+ let src_name = CString :: new ( "source.txt" ) . unwrap ( ) ;
1802+ let src_entry = fs. lookup ( & ctx, ROOT_ID , & src_name) . unwrap ( ) ;
1803+ let ( src_handle, _, _) = fs
1804+ . open ( & ctx, src_entry. inode , libc:: O_RDWR as u32 , 0 )
1805+ . unwrap ( ) ;
1806+ let src_handle = src_handle. expect ( "expected src handle" ) ;
1807+
1808+ let dst_name = CString :: new ( "dest.txt" ) . unwrap ( ) ;
1809+ let dst_entry = fs. lookup ( & ctx, ROOT_ID , & dst_name) . unwrap ( ) ;
1810+ let ( dst_handle, _, _) = fs
1811+ . open ( & ctx, dst_entry. inode , libc:: O_RDWR as u32 , 0 )
1812+ . unwrap ( ) ;
1813+ let dst_handle = dst_handle. expect ( "expected dst handle" ) ;
1814+
1815+ // Copy data from source to destination using copy_file_range
1816+ let copied = fs
1817+ . copy_file_range (
1818+ & ctx,
1819+ src_entry. inode ,
1820+ src_handle,
1821+ 0 , // offset_in
1822+ dst_entry. inode ,
1823+ dst_handle,
1824+ 0 , // offset_out
1825+ test_data. len ( ) as u64 ,
1826+ 0 , // flags
1827+ )
1828+ . unwrap ( ) ;
1829+ assert_eq ! ( copied, test_data. len( ) ) ;
1830+
1831+ // Sync and verify by reading directly from disk
1832+ fs. fsync ( & ctx, dst_entry. inode , false , dst_handle) . unwrap ( ) ;
1833+ let result = std:: fs:: read ( & dst_path) . unwrap ( ) ;
1834+ assert_eq ! ( & result, test_data) ;
1835+
1836+ // Test partial copy with offset
1837+ let offset = 7 ; // "Hello, " is 7 bytes
1838+ let partial_len = test_data. len ( ) - offset;
1839+ let copied = fs
1840+ . copy_file_range (
1841+ & ctx,
1842+ src_entry. inode ,
1843+ src_handle,
1844+ offset as u64 ,
1845+ dst_entry. inode ,
1846+ dst_handle,
1847+ test_data. len ( ) as u64 , // append after existing data
1848+ partial_len as u64 ,
1849+ 0 ,
1850+ )
1851+ . unwrap ( ) ;
1852+ assert_eq ! ( copied, partial_len) ;
1853+
1854+ // Verify the appended data
1855+ fs. fsync ( & ctx, dst_entry. inode , false , dst_handle) . unwrap ( ) ;
1856+ let result = std:: fs:: read ( & dst_path) . unwrap ( ) ;
1857+ assert_eq ! ( result. len( ) , test_data. len( ) + partial_len) ;
1858+ assert_eq ! ( & result[ ..test_data. len( ) ] , test_data) ;
1859+ assert_eq ! ( & result[ test_data. len( ) ..] , & test_data[ offset..] ) ;
1860+ }
17461861}
0 commit comments