1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/file/copy.c 5 * PURPOSE: Copying files 6 * PROGRAMMER: Ariadne (ariadne@xs4all.nl) 7 * UPDATE HISTORY: 8 * 01/11/98 Created 9 * 07/02/99 Moved to separate file 10 */ 11 12 /* INCLUDES ****************************************************************/ 13 14 #include <k32.h> 15 #define NDEBUG 16 #include <debug.h> 17 18 #if DBG 19 DEBUG_CHANNEL(kernel32file); 20 #endif 21 22 /* FUNCTIONS ****************************************************************/ 23 24 25 static NTSTATUS 26 CopyLoop ( 27 HANDLE FileHandleSource, 28 HANDLE FileHandleDest, 29 LARGE_INTEGER SourceFileSize, 30 LPPROGRESS_ROUTINE lpProgressRoutine, 31 LPVOID lpData, 32 BOOL *pbCancel, 33 BOOL *KeepDest 34 ) 35 { 36 NTSTATUS errCode; 37 IO_STATUS_BLOCK IoStatusBlock; 38 UCHAR *lpBuffer = NULL; 39 SIZE_T RegionSize = 0x10000; 40 LARGE_INTEGER BytesCopied; 41 DWORD CallbackReason; 42 DWORD ProgressResult; 43 BOOL EndOfFileFound; 44 45 *KeepDest = FALSE; 46 errCode = NtAllocateVirtualMemory(NtCurrentProcess(), 47 (PVOID *)&lpBuffer, 48 0, 49 &RegionSize, 50 MEM_RESERVE | MEM_COMMIT, 51 PAGE_READWRITE); 52 53 if (NT_SUCCESS(errCode)) 54 { 55 BytesCopied.QuadPart = 0; 56 EndOfFileFound = FALSE; 57 CallbackReason = CALLBACK_STREAM_SWITCH; 58 while (! EndOfFileFound && 59 NT_SUCCESS(errCode) && 60 (NULL == pbCancel || ! *pbCancel)) 61 { 62 if (NULL != lpProgressRoutine) 63 { 64 ProgressResult = (*lpProgressRoutine)(SourceFileSize, 65 BytesCopied, 66 SourceFileSize, 67 BytesCopied, 68 0, 69 CallbackReason, 70 FileHandleSource, 71 FileHandleDest, 72 lpData); 73 switch (ProgressResult) 74 { 75 case PROGRESS_CANCEL: 76 TRACE("Progress callback requested cancel\n"); 77 errCode = STATUS_REQUEST_ABORTED; 78 break; 79 case PROGRESS_STOP: 80 TRACE("Progress callback requested stop\n"); 81 errCode = STATUS_REQUEST_ABORTED; 82 *KeepDest = TRUE; 83 break; 84 case PROGRESS_QUIET: 85 lpProgressRoutine = NULL; 86 break; 87 case PROGRESS_CONTINUE: 88 default: 89 break; 90 } 91 CallbackReason = CALLBACK_CHUNK_FINISHED; 92 } 93 if (NT_SUCCESS(errCode)) 94 { 95 errCode = NtReadFile(FileHandleSource, 96 NULL, 97 NULL, 98 NULL, 99 (PIO_STATUS_BLOCK)&IoStatusBlock, 100 lpBuffer, 101 RegionSize, 102 NULL, 103 NULL); 104 /* With sync read, 0 length + status success mean EOF: 105 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx 106 */ 107 if (NT_SUCCESS(errCode) && IoStatusBlock.Information == 0) 108 { 109 errCode = STATUS_END_OF_FILE; 110 } 111 if (NT_SUCCESS(errCode) && (NULL == pbCancel || ! *pbCancel)) 112 { 113 errCode = NtWriteFile(FileHandleDest, 114 NULL, 115 NULL, 116 NULL, 117 (PIO_STATUS_BLOCK)&IoStatusBlock, 118 lpBuffer, 119 IoStatusBlock.Information, 120 NULL, 121 NULL); 122 if (NT_SUCCESS(errCode)) 123 { 124 BytesCopied.QuadPart += IoStatusBlock.Information; 125 } 126 else 127 { 128 WARN("Error 0x%08x reading writing to dest\n", errCode); 129 } 130 } 131 else if (!NT_SUCCESS(errCode)) 132 { 133 if (STATUS_END_OF_FILE == errCode) 134 { 135 EndOfFileFound = TRUE; 136 errCode = STATUS_SUCCESS; 137 } 138 else 139 { 140 WARN("Error 0x%08x reading from source\n", errCode); 141 } 142 } 143 } 144 } 145 146 if (! EndOfFileFound && (NULL != pbCancel && *pbCancel)) 147 { 148 TRACE("User requested cancel\n"); 149 errCode = STATUS_REQUEST_ABORTED; 150 } 151 152 NtFreeVirtualMemory(NtCurrentProcess(), 153 (PVOID *)&lpBuffer, 154 &RegionSize, 155 MEM_RELEASE); 156 } 157 else 158 { 159 TRACE("Error 0x%08x allocating buffer of %lu bytes\n", errCode, RegionSize); 160 } 161 162 return errCode; 163 } 164 165 static NTSTATUS 166 SetLastWriteTime( 167 HANDLE FileHandle, 168 LARGE_INTEGER LastWriteTime 169 ) 170 { 171 NTSTATUS errCode = STATUS_SUCCESS; 172 IO_STATUS_BLOCK IoStatusBlock; 173 FILE_BASIC_INFORMATION FileBasic; 174 175 errCode = NtQueryInformationFile (FileHandle, 176 &IoStatusBlock, 177 &FileBasic, 178 sizeof(FILE_BASIC_INFORMATION), 179 FileBasicInformation); 180 if (!NT_SUCCESS(errCode)) 181 { 182 WARN("Error 0x%08x obtaining FileBasicInformation\n", errCode); 183 } 184 else 185 { 186 FileBasic.LastWriteTime.QuadPart = LastWriteTime.QuadPart; 187 errCode = NtSetInformationFile (FileHandle, 188 &IoStatusBlock, 189 &FileBasic, 190 sizeof(FILE_BASIC_INFORMATION), 191 FileBasicInformation); 192 if (!NT_SUCCESS(errCode)) 193 { 194 WARN("Error 0x%0x setting LastWriteTime\n", errCode); 195 } 196 } 197 198 return errCode; 199 } 200 201 BOOL 202 BasepCopyFileExW(IN LPCWSTR lpExistingFileName, 203 IN LPCWSTR lpNewFileName, 204 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, 205 IN LPVOID lpData OPTIONAL, 206 IN LPBOOL pbCancel OPTIONAL, 207 IN DWORD dwCopyFlags, 208 IN DWORD dwBasepFlags, 209 OUT LPHANDLE lpExistingHandle, 210 OUT LPHANDLE lpNewHandle) 211 { 212 NTSTATUS errCode; 213 HANDLE FileHandleSource, FileHandleDest; 214 IO_STATUS_BLOCK IoStatusBlock; 215 FILE_STANDARD_INFORMATION FileStandard; 216 FILE_BASIC_INFORMATION FileBasic; 217 BOOL RC = FALSE; 218 BOOL KeepDestOnError = FALSE; 219 DWORD SystemError; 220 221 FileHandleSource = CreateFileW(lpExistingFileName, 222 GENERIC_READ, 223 FILE_SHARE_READ | FILE_SHARE_WRITE, 224 NULL, 225 OPEN_EXISTING, 226 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING, 227 NULL); 228 if (INVALID_HANDLE_VALUE != FileHandleSource) 229 { 230 errCode = NtQueryInformationFile(FileHandleSource, 231 &IoStatusBlock, 232 &FileStandard, 233 sizeof(FILE_STANDARD_INFORMATION), 234 FileStandardInformation); 235 if (!NT_SUCCESS(errCode)) 236 { 237 TRACE("Status 0x%08x obtaining FileStandardInformation for source\n", errCode); 238 BaseSetLastNTError(errCode); 239 } 240 else 241 { 242 errCode = NtQueryInformationFile(FileHandleSource, 243 &IoStatusBlock,&FileBasic, 244 sizeof(FILE_BASIC_INFORMATION), 245 FileBasicInformation); 246 if (!NT_SUCCESS(errCode)) 247 { 248 TRACE("Status 0x%08x obtaining FileBasicInformation for source\n", errCode); 249 BaseSetLastNTError(errCode); 250 } 251 else 252 { 253 FileHandleDest = CreateFileW(lpNewFileName, 254 GENERIC_WRITE, 255 FILE_SHARE_WRITE, 256 NULL, 257 dwCopyFlags ? CREATE_NEW : CREATE_ALWAYS, 258 FileBasic.FileAttributes, 259 NULL); 260 if (INVALID_HANDLE_VALUE != FileHandleDest) 261 { 262 errCode = CopyLoop(FileHandleSource, 263 FileHandleDest, 264 FileStandard.EndOfFile, 265 lpProgressRoutine, 266 lpData, 267 pbCancel, 268 &KeepDestOnError); 269 if (!NT_SUCCESS(errCode)) 270 { 271 BaseSetLastNTError(errCode); 272 } 273 else 274 { 275 LARGE_INTEGER t; 276 277 t.QuadPart = FileBasic.LastWriteTime.QuadPart; 278 errCode = SetLastWriteTime(FileHandleDest, t); 279 if (!NT_SUCCESS(errCode)) 280 { 281 BaseSetLastNTError(errCode); 282 } 283 else 284 { 285 RC = TRUE; 286 } 287 } 288 NtClose(FileHandleDest); 289 if (! RC && ! KeepDestOnError) 290 { 291 SystemError = GetLastError(); 292 SetFileAttributesW(lpNewFileName, FILE_ATTRIBUTE_NORMAL); 293 DeleteFileW(lpNewFileName); 294 SetLastError(SystemError); 295 } 296 } 297 else 298 { 299 WARN("Error %lu during opening of dest file\n", GetLastError()); 300 } 301 } 302 } 303 NtClose(FileHandleSource); 304 } 305 else 306 { 307 WARN("Error %lu during opening of source file\n", GetLastError()); 308 } 309 310 return RC; 311 } 312 313 /* 314 * @implemented 315 */ 316 BOOL 317 WINAPI 318 CopyFileExW(IN LPCWSTR lpExistingFileName, 319 IN LPCWSTR lpNewFileName, 320 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, 321 IN LPVOID lpData OPTIONAL, 322 IN LPBOOL pbCancel OPTIONAL, 323 IN DWORD dwCopyFlags) 324 { 325 BOOL Ret; 326 HANDLE ExistingHandle, NewHandle; 327 328 ExistingHandle = INVALID_HANDLE_VALUE; 329 NewHandle = INVALID_HANDLE_VALUE; 330 331 _SEH2_TRY 332 { 333 Ret = BasepCopyFileExW(lpExistingFileName, 334 lpNewFileName, 335 lpProgressRoutine, 336 lpData, 337 pbCancel, 338 dwCopyFlags, 339 0, 340 &ExistingHandle, 341 &NewHandle); 342 } 343 _SEH2_FINALLY 344 { 345 if (ExistingHandle != INVALID_HANDLE_VALUE) 346 { 347 CloseHandle(ExistingHandle); 348 } 349 350 if (NewHandle != INVALID_HANDLE_VALUE) 351 { 352 CloseHandle(NewHandle); 353 } 354 } 355 _SEH2_END; 356 357 return Ret; 358 } 359 360 361 /* 362 * @implemented 363 */ 364 BOOL 365 WINAPI 366 CopyFileExA(IN LPCSTR lpExistingFileName, 367 IN LPCSTR lpNewFileName, 368 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, 369 IN LPVOID lpData OPTIONAL, 370 IN LPBOOL pbCancel OPTIONAL, 371 IN DWORD dwCopyFlags) 372 { 373 BOOL Result = FALSE; 374 UNICODE_STRING lpNewFileNameW; 375 PUNICODE_STRING lpExistingFileNameW; 376 377 lpExistingFileNameW = Basep8BitStringToStaticUnicodeString(lpExistingFileName); 378 if (!lpExistingFileNameW) 379 { 380 return FALSE; 381 } 382 383 if (Basep8BitStringToDynamicUnicodeString(&lpNewFileNameW, lpNewFileName)) 384 { 385 Result = CopyFileExW(lpExistingFileNameW->Buffer, 386 lpNewFileNameW.Buffer, 387 lpProgressRoutine, 388 lpData, 389 pbCancel, 390 dwCopyFlags); 391 392 RtlFreeUnicodeString(&lpNewFileNameW); 393 } 394 395 return Result; 396 } 397 398 399 /* 400 * @implemented 401 */ 402 BOOL 403 WINAPI 404 CopyFileA(IN LPCSTR lpExistingFileName, 405 IN LPCSTR lpNewFileName, 406 IN BOOL bFailIfExists) 407 { 408 BOOL Result = FALSE; 409 UNICODE_STRING lpNewFileNameW; 410 PUNICODE_STRING lpExistingFileNameW; 411 412 lpExistingFileNameW = Basep8BitStringToStaticUnicodeString(lpExistingFileName); 413 if (!lpExistingFileNameW) 414 { 415 return FALSE; 416 } 417 418 if (Basep8BitStringToDynamicUnicodeString(&lpNewFileNameW, lpNewFileName)) 419 { 420 Result = CopyFileExW(lpExistingFileNameW->Buffer, 421 lpNewFileNameW.Buffer, 422 NULL, 423 NULL, 424 NULL, 425 (bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0)); 426 427 RtlFreeUnicodeString(&lpNewFileNameW); 428 } 429 430 return Result; 431 } 432 433 434 /* 435 * @implemented 436 */ 437 BOOL 438 WINAPI 439 CopyFileW(IN LPCWSTR lpExistingFileName, 440 IN LPCWSTR lpNewFileName, 441 IN BOOL bFailIfExists) 442 { 443 return CopyFileExW(lpExistingFileName, 444 lpNewFileName, 445 NULL, 446 NULL, 447 NULL, 448 (bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0)); 449 } 450 451 452 /* 453 * @implemented 454 */ 455 BOOL 456 WINAPI 457 PrivCopyFileExW(IN LPCWSTR lpExistingFileName, 458 IN LPCWSTR lpNewFileName, 459 IN LPPROGRESS_ROUTINE lpProgressRoutine, 460 IN LPVOID lpData, 461 IN LPBOOL pbCancel, 462 IN DWORD dwCopyFlags) 463 { 464 BOOL Ret; 465 HANDLE ExistingHandle, NewHandle; 466 467 ExistingHandle = INVALID_HANDLE_VALUE; 468 NewHandle = INVALID_HANDLE_VALUE; 469 470 /* Check for incompatible flags */ 471 if (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS && dwCopyFlags & BASEP_COPY_REPLACE) 472 { 473 SetLastError(ERROR_INVALID_PARAMETER); 474 return FALSE; 475 } 476 477 _SEH2_TRY 478 { 479 Ret = BasepCopyFileExW(lpExistingFileName, 480 lpNewFileName, 481 lpProgressRoutine, 482 lpData, 483 pbCancel, 484 dwCopyFlags & BASEP_COPY_PUBLIC_MASK, 485 dwCopyFlags & BASEP_COPY_BASEP_MASK, 486 &ExistingHandle, 487 &NewHandle); 488 } 489 _SEH2_FINALLY 490 { 491 if (ExistingHandle != INVALID_HANDLE_VALUE) 492 { 493 CloseHandle(ExistingHandle); 494 } 495 496 if (NewHandle != INVALID_HANDLE_VALUE) 497 { 498 CloseHandle(NewHandle); 499 } 500 } 501 _SEH2_END; 502 503 return Ret; 504 } 505 506 /* EOF */ 507