@@ -17,7 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
17
17
use crate :: thread;
18
18
19
19
use super :: path:: maybe_verbatim;
20
- use super :: { api, to_u16s, IoResult } ;
20
+ use super :: { api, compat , to_u16s, IoResult } ;
21
21
22
22
pub struct File {
23
23
handle : Handle ,
@@ -1098,8 +1098,22 @@ pub fn unlink(p: &Path) -> io::Result<()> {
1098
1098
pub fn rename ( old : & Path , new : & Path ) -> io:: Result < ( ) > {
1099
1099
let old = maybe_verbatim ( old) ?;
1100
1100
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
+ }
1103
1117
}
1104
1118
1105
1119
pub fn rmdir ( p : & Path ) -> io:: Result < ( ) > {
@@ -1402,36 +1416,91 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1402
1416
}
1403
1417
1404
1418
pub 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
- }
1421
1419
let pfrom = maybe_verbatim ( from) ?;
1422
1420
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
+ }
1435
1504
}
1436
1505
1437
1506
#[ allow( dead_code) ]
0 commit comments