@@ -17,7 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
1717use crate :: thread;
1818
1919use super :: path:: maybe_verbatim;
20- use super :: { api, to_u16s, IoResult } ;
20+ use super :: { api, compat , to_u16s, IoResult } ;
2121
2222pub struct File {
2323 handle : Handle ,
@@ -1098,8 +1098,22 @@ pub fn unlink(p: &Path) -> io::Result<()> {
10981098pub fn rename ( old : & Path , new : & Path ) -> io:: Result < ( ) > {
10991099 let old = maybe_verbatim ( old) ?;
11001100 let new = maybe_verbatim ( new) ?;
1101- cvt ( unsafe { c:: MoveFileExW ( old. as_ptr ( ) , new. as_ptr ( ) , c:: MOVEFILE_REPLACE_EXISTING ) } ) ?;
1102- Ok ( ( ) )
1101+ let res =
1102+ cvt ( unsafe { c:: MoveFileExW ( old. as_ptr ( ) , new. as_ptr ( ) , c:: MOVEFILE_REPLACE_EXISTING ) } ) ;
1103+
1104+ match res {
1105+ Err ( e) if e. raw_os_error ( ) == Some ( c:: ERROR_CALL_NOT_IMPLEMENTED as i32 ) => {
1106+ // 9x/ME doesn't support MoveFileEx, so we fall back to copy + delete and hope for the
1107+ // best
1108+ unsafe {
1109+ cvt ( c:: CopyFileW ( old. as_ptr ( ) , new. as_ptr ( ) , c:: TRUE ) ) ?;
1110+ cvt ( c:: DeleteFileW ( old. as_ptr ( ) ) ) ?;
1111+ Ok ( ( ) )
1112+ }
1113+ }
1114+ Err ( e) => Err ( e) ,
1115+ Ok ( _) => Ok ( ( ) ) ,
1116+ }
11031117}
11041118
11051119pub fn rmdir ( p : & Path ) -> io:: Result < ( ) > {
@@ -1402,36 +1416,91 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
14021416}
14031417
14041418pub fn copy ( from : & Path , to : & Path ) -> io:: Result < u64 > {
1405- unsafe extern "system" fn callback (
1406- _TotalFileSize : c:: LARGE_INTEGER ,
1407- _TotalBytesTransferred : c:: LARGE_INTEGER ,
1408- _StreamSize : c:: LARGE_INTEGER ,
1409- StreamBytesTransferred : c:: LARGE_INTEGER ,
1410- dwStreamNumber : c:: DWORD ,
1411- _dwCallbackReason : c:: DWORD ,
1412- _hSourceFile : c:: HANDLE ,
1413- _hDestinationFile : c:: HANDLE ,
1414- lpData : c:: LPCVOID ,
1415- ) -> c:: DWORD {
1416- if dwStreamNumber == 1 {
1417- * ( lpData as * mut i64 ) = StreamBytesTransferred ;
1418- }
1419- c:: PROGRESS_CONTINUE
1420- }
14211419 let pfrom = maybe_verbatim ( from) ?;
14221420 let pto = maybe_verbatim ( to) ?;
1423- let mut size = 0i64 ;
1424- cvt ( unsafe {
1425- c:: CopyFileExW (
1426- pfrom. as_ptr ( ) ,
1427- pto. as_ptr ( ) ,
1428- Some ( callback) ,
1429- & mut size as * mut _ as * mut _ ,
1430- ptr:: null_mut ( ) ,
1431- 0 ,
1432- )
1433- } ) ?;
1434- Ok ( size as u64 )
1421+
1422+ // NT 4+
1423+ //
1424+ // Unicows implements CopyFileExW similarly to other functions (convert to ANSI, call ...A API).
1425+ // However, 9x/ME don't support CopyFileExA either. This means that we have to check both for
1426+ // the API to exist *and* that we're running on NT.
1427+ if c:: CopyFileExW :: option ( ) . is_some ( ) && compat:: is_windows_nt ( ) {
1428+ unsafe extern "system" fn callback (
1429+ _TotalFileSize : c:: LARGE_INTEGER ,
1430+ _TotalBytesTransferred : c:: LARGE_INTEGER ,
1431+ _StreamSize : c:: LARGE_INTEGER ,
1432+ StreamBytesTransferred : c:: LARGE_INTEGER ,
1433+ dwStreamNumber : c:: DWORD ,
1434+ _dwCallbackReason : c:: DWORD ,
1435+ _hSourceFile : c:: HANDLE ,
1436+ _hDestinationFile : c:: HANDLE ,
1437+ lpData : c:: LPCVOID ,
1438+ ) -> c:: DWORD {
1439+ if dwStreamNumber == 1 {
1440+ * ( lpData as * mut i64 ) = StreamBytesTransferred ;
1441+ }
1442+ c:: PROGRESS_CONTINUE
1443+ }
1444+
1445+ let mut size = 0i64 ;
1446+ cvt ( unsafe {
1447+ c:: CopyFileExW (
1448+ pfrom. as_ptr ( ) ,
1449+ pto. as_ptr ( ) ,
1450+ Some ( callback) ,
1451+ & mut size as * mut _ as * mut _ ,
1452+ ptr:: null_mut ( ) ,
1453+ 0 ,
1454+ )
1455+ } ) ?;
1456+ Ok ( size as u64 )
1457+ } else {
1458+ // NT 3.51 and earlier, or 9x/ME
1459+
1460+ // If `CopyFileExW` is not available, we have to copy the file with the non-Ex API,
1461+ // then open it with `dwDesiredAccess = 0` (query attributes only),
1462+ // then use `GetFileSize` to retrieve the size
1463+ cvt ( unsafe {
1464+ c:: CopyFileW (
1465+ pfrom. as_ptr ( ) ,
1466+ pto. as_ptr ( ) ,
1467+ c:: FALSE , // FALSE: allow overwriting
1468+ )
1469+ } ) ?;
1470+
1471+ let handle = unsafe {
1472+ c:: CreateFileW (
1473+ pto. as_ptr ( ) ,
1474+ 0 ,
1475+ c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE ,
1476+ ptr:: null_mut ( ) ,
1477+ c:: OPEN_EXISTING ,
1478+ 0 ,
1479+ ptr:: null_mut ( ) ,
1480+ )
1481+ } ;
1482+
1483+ let handle = if let Ok ( handle) =
1484+ OwnedHandle :: try_from ( unsafe { HandleOrInvalid :: from_raw_handle ( handle) } )
1485+ {
1486+ handle
1487+ } else {
1488+ return Err ( Error :: last_os_error ( ) ) ;
1489+ } ;
1490+
1491+ let mut upper_u32: u32 = 0 ;
1492+ let lower_u32 = unsafe { c:: GetFileSize ( handle. as_raw_handle ( ) , & mut upper_u32) } ;
1493+
1494+ // 0xFFFFFFFF might be a valid length, so we have to check GetLastError
1495+ if lower_u32 == c:: INVALID_FILE_SIZE {
1496+ let error = api:: get_last_error ( ) ;
1497+ if error. code != c:: ERROR_SUCCESS {
1498+ return Err ( Error :: from_raw_os_error ( error. code as i32 ) ) ;
1499+ }
1500+ }
1501+
1502+ Ok ( ( upper_u32 as u64 ) << 32 | lower_u32 as u64 )
1503+ }
14351504}
14361505
14371506#[ allow( dead_code) ]
0 commit comments