1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/file/move.c 5 * PURPOSE: Directory functions 6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl) 7 * Gerhard W. Gruber (sparhawk_at_gmx.at) 8 * Dmitry Philippov (shedon@mail.ru) 9 * Pierre Schweitzer (pierre@reactos.org) 10 * UPDATE HISTORY: 11 * Created 01/11/98 12 * DP (29/07/2006) 13 * Fix some bugs in the add_boot_rename_entry function 14 */ 15 16 /* INCLUDES *****************************************************************/ 17 18 #include <k32.h> 19 #include <malloc.h> 20 #include <strsafe.h> 21 #define NDEBUG 22 #include <debug.h> 23 DEBUG_CHANNEL(kernel32file); 24 25 /* GLOBALS *****************************************************************/ 26 27 /* DEFINES *****************************************************************/ 28 typedef struct _COPY_PROGRESS_CONTEXT 29 { 30 ULONG Flags; 31 LPPROGRESS_ROUTINE UserRoutine; 32 LPVOID UserData; 33 } COPY_PROGRESS_CONTEXT, *PCOPY_PROGRESS_CONTEXT; 34 35 /* FUNCTIONS ****************************************************************/ 36 /* 37 * @implemented 38 */ 39 NTSTATUS 40 WINAPI 41 BasepMoveFileDelayed(IN PUNICODE_STRING ExistingPath, 42 IN PUNICODE_STRING NewPath, 43 IN INT KeyId, 44 IN BOOL CreateIfNotFound) 45 { 46 #define STRING_LENGTH 0x400 47 NTSTATUS Status; 48 HANDLE KeyHandle; 49 PVOID Buffer, BufferBegin; 50 OBJECT_ATTRIBUTES ObjectAttributes; 51 PWSTR PendingOperations, BufferWrite; 52 ULONG DataSize, BufferLength, StringLength = STRING_LENGTH; 53 UNICODE_STRING SessionManagerString, PendingOperationsString; 54 /* +6 because a INT shouldn't take more than 6 chars. Especially given the call path */ 55 WCHAR PendingOperationsBuffer[sizeof(L"PendingFileRenameOperations") / sizeof(WCHAR) + 6]; 56 57 RtlInitUnicodeString(&SessionManagerString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"); 58 59 /* Select appropriate key for adding our file */ 60 if (KeyId == 1) 61 { 62 PendingOperations = L"PendingFileRenameOperations"; 63 } 64 else 65 { 66 StringCbPrintfW(PendingOperationsBuffer, sizeof(PendingOperationsBuffer), L"PendingFileRenameOperations%d", KeyId); 67 PendingOperations = PendingOperationsBuffer; 68 } 69 RtlInitUnicodeString(&PendingOperationsString, PendingOperations); 70 71 InitializeObjectAttributes(&ObjectAttributes, 72 &SessionManagerString, 73 OBJ_OPENIF | OBJ_CASE_INSENSITIVE, 74 NULL, NULL); 75 76 /* Open parent key */ 77 Status = NtCreateKey(&KeyHandle, 78 GENERIC_READ | GENERIC_WRITE, 79 &ObjectAttributes, 0, NULL, 80 REG_OPTION_NON_VOLATILE, NULL); 81 if (Status == STATUS_ACCESS_DENIED) 82 { 83 Status = NtCreateKey(&KeyHandle, 84 GENERIC_READ | GENERIC_WRITE, 85 &ObjectAttributes, 0, NULL, 86 REG_OPTION_BACKUP_RESTORE, NULL); 87 } 88 89 if (!NT_SUCCESS(Status)) 90 { 91 return Status; 92 } 93 94 /* Reserve enough to read previous string + to append our with required null chars */ 95 BufferLength = NewPath->Length + ExistingPath->Length + STRING_LENGTH + 3 * sizeof(UNICODE_NULL); 96 97 while (TRUE) 98 { 99 /* Allocate output buffer */ 100 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); 101 if (Buffer == NULL) 102 { 103 NtClose(KeyHandle); 104 return STATUS_NO_MEMORY; 105 } 106 107 Status = NtQueryValueKey(KeyHandle, 108 &PendingOperationsString, 109 KeyValuePartialInformation, 110 Buffer, StringLength, &DataSize); 111 if (Status != STATUS_BUFFER_OVERFLOW) 112 { 113 break; 114 } 115 116 /* If buffer was too small, then, reallocate one which is big enough */ 117 StringLength = DataSize; 118 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 119 BufferLength = ExistingPath->Length + StringLength + NewPath->Length + 3 * sizeof(UNICODE_NULL); 120 /* Check we didn't overflow */ 121 if (BufferLength < StringLength) 122 { 123 NtClose(KeyHandle); 124 return STATUS_BUFFER_TOO_SMALL; 125 } 126 } 127 128 /* Check if it existed - if not, create only IF asked to */ 129 if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND || !CreateIfNotFound)) 130 { 131 NtClose(KeyHandle); 132 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 133 return Status; 134 } 135 136 if (!NT_SUCCESS(Status)) 137 { 138 /* We didn't find any - ie, we create, so use complete buffer */ 139 BufferBegin = Buffer; 140 BufferWrite = Buffer; 141 } 142 else 143 { 144 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer; 145 146 /* Get data, our buffer begin and then where we should append data 147 * (- null char, this is REG_MULTI_SZ, it already includes double termination, we keep only one) 148 */ 149 BufferBegin = PartialInfo->Data; 150 BufferWrite = (PWSTR)((ULONG_PTR)PartialInfo->Data + PartialInfo->DataLength - sizeof(UNICODE_NULL)); 151 } 152 153 /* First copy existing */ 154 RtlCopyMemory(BufferWrite, ExistingPath->Buffer, ExistingPath->Length); 155 BufferWrite += ExistingPath->Length / sizeof(WCHAR); 156 /* And append null char */ 157 *BufferWrite = UNICODE_NULL; 158 ++BufferWrite; 159 /* Append destination */ 160 RtlCopyMemory(BufferWrite, NewPath->Buffer, NewPath->Length); 161 BufferWrite += NewPath->Length / sizeof(WCHAR); 162 /* And append two null char (end of string) */ 163 *BufferWrite = UNICODE_NULL; 164 ++BufferWrite; 165 *BufferWrite = UNICODE_NULL; 166 167 /* Set new value */ 168 Status = NtSetValueKey(KeyHandle, 169 &PendingOperationsString, 170 0, REG_MULTI_SZ, BufferBegin, 171 (ULONG_PTR)BufferWrite - (ULONG_PTR)BufferBegin + sizeof(WCHAR)); 172 173 NtClose(KeyHandle); 174 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 175 176 return Status; 177 } 178 179 180 /* 181 * @implemented 182 */ 183 DWORD 184 WINAPI 185 BasepGetComputerNameFromNtPath(IN PUNICODE_STRING NewPath, 186 IN HANDLE NewHandle, 187 OUT PWSTR ComputerName, 188 IN OUT PULONG ComputerNameLength) 189 { 190 BOOL Query = FALSE; 191 WCHAR Letter; 192 PWSTR AbsolutePath, EndOfName; 193 USHORT AbsolutePathLength, NameLength; 194 WCHAR TargetDevice[0x105]; 195 WCHAR DeviceName[] = {'A', ':', '\0'}; /* Init to something, will be set later */ 196 UNICODE_STRING UncString = RTL_CONSTANT_STRING(L"\\??\\UNC\\"); 197 UNICODE_STRING GlobalString = RTL_CONSTANT_STRING(L"\\??\\"); 198 199 DPRINT("BasepGetComputerNameFromNtPath(%wZ, %p, %p, %lu)\n", NewPath, NewHandle, ComputerName, ComputerNameLength); 200 201 /* If it's an UNC path */ 202 if (RtlPrefixUnicodeString(&UncString, NewPath, TRUE)) 203 { 204 /* Check for broken caller */ 205 if (NewPath->Length <= UncString.Length) 206 { 207 return ERROR_BAD_PATHNAME; 208 } 209 210 /* Skip UNC prefix */ 211 AbsolutePath = &NewPath->Buffer[UncString.Length / sizeof(WCHAR)]; 212 AbsolutePathLength = NewPath->Length - UncString.Length; 213 214 /* And query DFS */ 215 Query = TRUE; 216 } 217 /* Otherwise, we have to be in global (NT path!), with drive letter */ 218 else if (RtlPrefixUnicodeString(&GlobalString, NewPath, TRUE) && NewPath->Buffer[5] == ':') 219 { 220 /* Path is like that: \??\C:\Complete Path\To File.ext */ 221 /* Get the letter and upcase it if required */ 222 Letter = NewPath->Buffer[4]; 223 if (Letter >= 'a' && Letter <= 'z') 224 { 225 Letter -= ('a' - 'A'); 226 } 227 DeviceName[0] = Letter; 228 229 /* Query the associated DOS device */ 230 if (!QueryDosDeviceW(DeviceName, TargetDevice, ARRAYSIZE(TargetDevice))) 231 { 232 return GetLastError(); 233 } 234 235 /* If that's a network share */ 236 if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\LanmanRedirector\\;")) 237 { 238 /* Path is like that: \Device\LanmanRedirector\;C:0000000000000000\Complete Path\To File.ext */ 239 /* Check we have the correct drive letter */ 240 if (TargetDevice[26] == DeviceName[0] && 241 TargetDevice[27] == ':') 242 { 243 /* Check for the path begin, computer name is before */ 244 PWSTR Path = wcschr(&TargetDevice[28], '\\'); 245 if (Path == NULL) 246 { 247 return ERROR_BAD_PATHNAME; 248 } 249 250 AbsolutePath = Path + 1; 251 AbsolutePathLength = sizeof(WCHAR) * (ARRAYSIZE(TargetDevice) - (AbsolutePath - TargetDevice)); 252 } 253 else 254 { 255 return ERROR_BAD_PATHNAME; 256 } 257 } 258 /* If it's a local device */ 259 else if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\Harddisk") 260 || TargetDevice == wcsstr(TargetDevice, L"\\Device\\CdRom") 261 || TargetDevice == wcsstr(TargetDevice, L"\\Device\\Floppy")) 262 { 263 /* Just query the computer name */ 264 if (!GetComputerNameW(ComputerName, ComputerNameLength)) 265 { 266 return GetLastError(); 267 } 268 269 return ERROR_SUCCESS; 270 } 271 /* If it's a DFS share */ 272 else if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\WinDfs\\")) 273 { 274 /* Obviously, query DFS */ 275 Query = TRUE; 276 } 277 else 278 { 279 return ERROR_BAD_PATHNAME; 280 } 281 } 282 else 283 { 284 return ERROR_BAD_PATHNAME; 285 } 286 287 /* Query DFS, currently not implemented - shouldn't be missing in ReactOS yet ;-) */ 288 if (Query) 289 { 290 UNIMPLEMENTED_DBGBREAK("Querying DFS not implemented!\n"); 291 AbsolutePath = NULL; 292 AbsolutePathLength = 0; 293 } 294 295 /* Now, properly extract the computer name from the full path */ 296 EndOfName = AbsolutePath; 297 if (AbsolutePathLength) 298 { 299 for (NameLength = 0; NameLength < AbsolutePathLength; NameLength += sizeof(WCHAR)) 300 { 301 /* Look for the next \, it will be the end of computer name */ 302 if (EndOfName[0] == '\\') 303 { 304 break; 305 } 306 /* Computer name cannot contain ., if we get to that point, something went wrong... */ 307 else if (EndOfName[0] == '.') 308 { 309 return ERROR_BAD_PATHNAME; 310 } 311 312 ++EndOfName; 313 } 314 } 315 316 NameLength = EndOfName - AbsolutePath; 317 /* Check we didn't overflow and that our computer name isn't ill-formed */ 318 if (NameLength >= AbsolutePathLength || NameLength >= MAX_COMPUTERNAME_LENGTH * sizeof(WCHAR)) 319 { 320 return ERROR_BAD_PATHNAME; 321 } 322 323 /* Check we can fit */ 324 if (NameLength + sizeof(UNICODE_NULL) > *ComputerNameLength * sizeof(WCHAR)) 325 { 326 return ERROR_BUFFER_OVERFLOW; 327 } 328 329 /* Write, zero and done! */ 330 RtlCopyMemory(ComputerName, AbsolutePath, NameLength); 331 *ComputerNameLength = NameLength / sizeof(WCHAR); 332 ComputerName[NameLength / sizeof(WCHAR)] = UNICODE_NULL; 333 334 return ERROR_SUCCESS; 335 } 336 337 338 /* 339 * @implemented 340 */ 341 NTSTATUS 342 WINAPI 343 BasepNotifyTrackingService(IN OUT PHANDLE ExistingHandle, 344 IN POBJECT_ATTRIBUTES ObjectAttributes, 345 IN HANDLE NewHandle, 346 IN PUNICODE_STRING NewPath) 347 { 348 NTSTATUS Status; 349 ULONG ComputerNameLength, FileAttributes; 350 WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; 351 OEM_STRING ComputerNameStringA; 352 CHAR ComputerNameStringBuffer[0x105]; 353 UNICODE_STRING ComputerNameStringW; 354 IO_STATUS_BLOCK IoStatusBlock; 355 FILE_BASIC_INFORMATION FileBasicInfo; 356 HANDLE hFullWrite; 357 struct 358 { 359 FILE_TRACKING_INFORMATION; 360 CHAR Buffer[(MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR)]; 361 } FileTrackingInfo; 362 363 DPRINT("BasepNotifyTrackingService(%p, %p, %p, %wZ)\n", *ExistingHandle, ObjectAttributes, NewHandle, NewPath); 364 365 Status = STATUS_SUCCESS; 366 ComputerNameLength = ARRAYSIZE(ComputerName); 367 368 /* Attempt to get computer name of target handle */ 369 if (BasepGetComputerNameFromNtPath(NewPath, NewHandle, ComputerName, &ComputerNameLength)) 370 { 371 /* If we failed to get it, we will just notify with the handle */ 372 FileTrackingInfo.ObjectInformationLength = 0; 373 } 374 else 375 { 376 /* Convert the retrieved computer name to ANSI and attach it to the notification */ 377 ComputerNameStringA.Length = 0; 378 ComputerNameStringA.MaximumLength = ARRAYSIZE(ComputerNameStringBuffer); 379 ComputerNameStringA.Buffer = ComputerNameStringBuffer; 380 381 RtlInitUnicodeString(&ComputerNameStringW, ComputerName); 382 Status = RtlUnicodeStringToOemString(&ComputerNameStringA, &ComputerNameStringW, 0); 383 if (!NT_SUCCESS(Status)) 384 { 385 return Status; 386 } 387 388 RtlCopyMemory(FileTrackingInfo.ObjectInformation, ComputerNameStringA.Buffer, ComputerNameStringA.Length); 389 FileTrackingInfo.ObjectInformation[ComputerNameStringA.Length] = 0; 390 FileTrackingInfo.ObjectInformationLength = ComputerNameStringA.Length + 1; 391 } 392 393 /* Attach the handle we moved */ 394 FileTrackingInfo.DestinationFile = NewHandle; 395 396 /* Final, notify */ 397 Status = NtSetInformationFile(*ExistingHandle, 398 &IoStatusBlock, 399 &FileTrackingInfo, 400 sizeof(FileTrackingInfo), 401 FileTrackingInformation); 402 if (Status != STATUS_ACCESS_DENIED) 403 { 404 return Status; 405 } 406 407 /* If we get here, we got access denied error, this comes from a 408 * read-only flag. So, close the file, in order to reopen it with enough 409 * rights to remove said flag and reattempt notification 410 */ 411 CloseHandle(*ExistingHandle); 412 413 /* Reopen it, to be able to change the destination file attributes */ 414 Status = NtOpenFile(ExistingHandle, 415 SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 416 ObjectAttributes, 417 &IoStatusBlock, 418 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 419 FILE_SYNCHRONOUS_IO_NONALERT); 420 if (!NT_SUCCESS(Status)) 421 { 422 *ExistingHandle = INVALID_HANDLE_VALUE; 423 return Status; 424 } 425 426 /* Get the file attributes */ 427 Status = NtQueryInformationFile(*ExistingHandle, 428 &IoStatusBlock, 429 &FileBasicInfo, 430 sizeof(FileBasicInfo), 431 FileBasicInformation); 432 if (!NT_SUCCESS(Status)) 433 { 434 return Status; 435 } 436 437 /* Get rid of the read only flag */ 438 FileAttributes = FileBasicInfo.FileAttributes & ~FILE_ATTRIBUTE_READONLY; 439 RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo)); 440 FileBasicInfo.FileAttributes = FileAttributes; 441 442 /* Attempt... */ 443 Status = NtSetInformationFile(*ExistingHandle, 444 &IoStatusBlock, 445 &FileBasicInfo, 446 sizeof(FileBasicInfo), 447 FileBasicInformation); 448 if (!NT_SUCCESS(Status)) 449 { 450 return Status; 451 } 452 453 /* Now, reopen with maximum accesses to notify */ 454 Status = NtOpenFile(&hFullWrite, 455 GENERIC_WRITE | SYNCHRONIZE, 456 ObjectAttributes, 457 &IoStatusBlock, 458 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 459 FILE_SYNCHRONOUS_IO_NONALERT); 460 if (NT_SUCCESS(Status)) 461 { 462 NtClose(*ExistingHandle); 463 *ExistingHandle = hFullWrite; 464 465 /* Full success, notify! */ 466 Status = NtSetInformationFile(*ExistingHandle, 467 &IoStatusBlock, 468 &FileTrackingInfo, 469 sizeof(FileTrackingInfo), 470 FileTrackingInformation); 471 } 472 473 /* If opening with full access failed or if notify failed, restore read-only */ 474 if (!NT_SUCCESS(Status)) 475 { 476 FileBasicInfo.FileAttributes |= FILE_ATTRIBUTE_READONLY; 477 478 Status = NtSetInformationFile(*ExistingHandle, 479 &IoStatusBlock, 480 &FileBasicInfo, 481 sizeof(FileBasicInfo), 482 FileBasicInformation); 483 } 484 485 /* We're done */ 486 return Status; 487 } 488 489 490 /* 491 * @implemented 492 */ 493 NTSTATUS 494 WINAPI 495 BasepOpenFileForMove(IN LPCWSTR File, 496 OUT PUNICODE_STRING RelativeNtName, 497 OUT LPWSTR * NtName, 498 OUT PHANDLE FileHandle, 499 OUT POBJECT_ATTRIBUTES ObjectAttributes, 500 IN ACCESS_MASK DesiredAccess, 501 IN ULONG ShareAccess, 502 IN ULONG OpenOptions) 503 { 504 RTL_RELATIVE_NAME_U RelativeName; 505 NTSTATUS Status; 506 IO_STATUS_BLOCK IoStatusBlock; 507 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo; 508 ULONG IntShareAccess; 509 BOOLEAN HasRelative = FALSE; 510 511 _SEH2_TRY 512 { 513 /* Zero output */ 514 RelativeNtName->Length = 515 RelativeNtName->MaximumLength = 0; 516 RelativeNtName->Buffer = NULL; 517 *NtName = NULL; 518 519 if (!RtlDosPathNameToRelativeNtPathName_U(File, RelativeNtName, NULL, &RelativeName)) 520 { 521 Status = STATUS_OBJECT_PATH_NOT_FOUND; 522 _SEH2_LEAVE; 523 } 524 525 HasRelative = TRUE; 526 *NtName = RelativeNtName->Buffer; 527 528 if (RelativeName.RelativeName.Length) 529 { 530 RelativeNtName->Length = RelativeName.RelativeName.Length; 531 RelativeNtName->MaximumLength = RelativeName.RelativeName.MaximumLength; 532 RelativeNtName->Buffer = RelativeName.RelativeName.Buffer; 533 } 534 else 535 { 536 RelativeName.ContainingDirectory = 0; 537 } 538 539 InitializeObjectAttributes(ObjectAttributes, 540 RelativeNtName, 541 OBJ_CASE_INSENSITIVE, 542 RelativeName.ContainingDirectory, 543 NULL); 544 /* Force certain flags here, given ops we'll do */ 545 IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE; 546 OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT; 547 548 /* We'll try to read reparse tag */ 549 Status = NtOpenFile(FileHandle, 550 DesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE, 551 ObjectAttributes, 552 &IoStatusBlock, 553 IntShareAccess, 554 OpenOptions | FILE_OPEN_REPARSE_POINT); 555 if (NT_SUCCESS(Status)) 556 { 557 /* Attempt the read */ 558 Status = NtQueryInformationFile(*FileHandle, 559 &IoStatusBlock, 560 &TagInfo, 561 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), 562 FileAttributeTagInformation); 563 564 /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */ 565 if (!NT_SUCCESS(Status) && 566 (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER)) 567 { 568 _SEH2_LEAVE; 569 } 570 571 if (NT_SUCCESS(Status)) 572 { 573 /* This cannot happen on mount points */ 574 if (TagInfo.FileAttributes & FILE_ATTRIBUTE_DEVICE || 575 TagInfo.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) 576 { 577 _SEH2_LEAVE; 578 } 579 } 580 581 NtClose(*FileHandle); 582 *FileHandle = INVALID_HANDLE_VALUE; 583 584 IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_DELETE; 585 } 586 else if (Status == STATUS_INVALID_PARAMETER) 587 { 588 IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE; 589 } 590 else 591 { 592 _SEH2_LEAVE; 593 } 594 595 /* Reattempt to open normally, following reparse point if needed */ 596 Status = NtOpenFile(FileHandle, 597 DesiredAccess | SYNCHRONIZE, 598 ObjectAttributes, 599 &IoStatusBlock, 600 IntShareAccess, 601 OpenOptions); 602 } 603 _SEH2_FINALLY 604 { 605 if (HasRelative) 606 { 607 RtlReleaseRelativeName(&RelativeName); 608 } 609 } 610 _SEH2_END; 611 612 return Status; 613 } 614 615 616 /* 617 * @implemented 618 */ 619 DWORD 620 WINAPI 621 BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize, 622 IN LARGE_INTEGER TotalBytesTransferred, 623 IN LARGE_INTEGER StreamSize, 624 IN LARGE_INTEGER StreamBytesTransferred, 625 IN DWORD dwStreamNumber, 626 IN DWORD dwCallbackReason, 627 IN HANDLE hSourceFile, 628 IN HANDLE hDestinationFile, 629 IN LPVOID lpData OPTIONAL) 630 { 631 DWORD Ret = 0; 632 PCOPY_PROGRESS_CONTEXT Context = (PCOPY_PROGRESS_CONTEXT)lpData; 633 634 if (Context->Flags & MOVEFILE_WRITE_THROUGH) 635 { 636 if (!dwCallbackReason) 637 { 638 if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart) 639 { 640 FlushFileBuffers(hDestinationFile); 641 } 642 } 643 } 644 645 if (Context->UserRoutine) 646 { 647 Ret = Context->UserRoutine(TotalFileSize, 648 TotalBytesTransferred, 649 StreamSize, 650 StreamBytesTransferred, 651 dwStreamNumber, 652 dwCallbackReason, 653 hSourceFile, 654 hDestinationFile, 655 Context->UserData); 656 } 657 658 return Ret; 659 } 660 661 662 /* 663 * @implemented 664 */ 665 BOOL 666 WINAPI 667 MoveFileWithProgressW(IN LPCWSTR lpExistingFileName, 668 IN LPCWSTR lpNewFileName, 669 IN LPPROGRESS_ROUTINE lpProgressRoutine, 670 IN LPVOID lpData, 671 IN DWORD dwFlags) 672 { 673 NTSTATUS Status; 674 PWSTR NewBuffer; 675 IO_STATUS_BLOCK IoStatusBlock; 676 COPY_PROGRESS_CONTEXT CopyContext; 677 OBJECT_ATTRIBUTES ObjectAttributes; 678 PFILE_RENAME_INFORMATION RenameInfo; 679 UNICODE_STRING NewPathU, ExistingPathU; 680 FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo; 681 HANDLE SourceHandle = INVALID_HANDLE_VALUE, NewHandle, ExistingHandle; 682 BOOL Ret = FALSE, ReplaceIfExists, DelayUntilReboot, AttemptReopenWithoutReparse; 683 684 DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags); 685 686 NewPathU.Buffer = NULL; 687 ExistingPathU.Buffer = NULL; 688 689 _SEH2_TRY 690 { 691 /* Don't allow renaming to a disk */ 692 if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName)) 693 { 694 BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION); 695 _SEH2_LEAVE; 696 } 697 698 ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING); 699 700 /* Get file path */ 701 if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL)) 702 { 703 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND); 704 _SEH2_LEAVE; 705 } 706 707 /* Sanitize input */ 708 DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT); 709 if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK)) 710 { 711 BaseSetLastNTError(STATUS_INVALID_PARAMETER); 712 _SEH2_LEAVE; 713 } 714 715 /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */ 716 AttemptReopenWithoutReparse = TRUE; 717 InitializeObjectAttributes(&ObjectAttributes, 718 &ExistingPathU, 719 OBJ_CASE_INSENSITIVE, 720 NULL, 721 NULL); 722 /* Attempt to open source file */ 723 Status = NtOpenFile(&SourceHandle, 724 FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, 725 &ObjectAttributes, 726 &IoStatusBlock, 727 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 728 FILE_OPEN_FOR_BACKUP_INTENT | ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0)); 729 if (!NT_SUCCESS(Status)) 730 { 731 /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */ 732 if (DelayUntilReboot && 733 (Status == STATUS_SHARING_VIOLATION || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND)) 734 { 735 /* Here we don't fail completely, as we postpone the operation to reboot 736 * File might exist afterwards, and we don't need a handle here 737 */ 738 SourceHandle = INVALID_HANDLE_VALUE; 739 AttemptReopenWithoutReparse = FALSE; 740 } 741 /* If we failed for any reason than unsupported reparse, fail completely */ 742 else if (Status != STATUS_INVALID_PARAMETER) 743 { 744 BaseSetLastNTError(Status); 745 _SEH2_LEAVE; 746 } 747 } 748 else 749 { 750 /* We managed to open, so query information */ 751 Status = NtQueryInformationFile(SourceHandle, 752 &IoStatusBlock, 753 &FileAttrTagInfo, 754 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), 755 FileAttributeTagInformation); 756 if (!NT_SUCCESS(Status)) 757 { 758 /* Do not tolerate any other error than something related to not supported operation */ 759 if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER) 760 { 761 BaseSetLastNTError(Status); 762 _SEH2_LEAVE; 763 } 764 765 /* Not a reparse point, no need to reopen, it's fine */ 766 AttemptReopenWithoutReparse = FALSE; 767 } 768 /* Validate the reparse point (do we support it?) */ 769 else if (FileAttrTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && 770 FileAttrTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 771 { 772 NtClose(SourceHandle); 773 SourceHandle = INVALID_HANDLE_VALUE; 774 } 775 else 776 { 777 /* Mount point, let's rename it */ 778 AttemptReopenWithoutReparse = FALSE; 779 } 780 } 781 782 /* Simply reopen if required */ 783 if (AttemptReopenWithoutReparse) 784 { 785 Status = NtOpenFile(&SourceHandle, 786 DELETE | SYNCHRONIZE, 787 &ObjectAttributes, 788 &IoStatusBlock, 789 FILE_SHARE_READ | FILE_SHARE_WRITE, 790 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0)); 791 if (!NT_SUCCESS(Status)) 792 { 793 BaseSetLastNTError(Status); 794 _SEH2_LEAVE; 795 } 796 } 797 798 /* Nullify string if we're to use it */ 799 if (DelayUntilReboot && !lpNewFileName) 800 { 801 RtlInitUnicodeString(&NewPathU, 0); 802 } 803 /* Check whether path exists */ 804 else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0)) 805 { 806 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND); 807 _SEH2_LEAVE; 808 } 809 810 /* Handle postponed renaming */ 811 if (DelayUntilReboot) 812 { 813 /* If new file exists and we're allowed to replace, then mark the path with ! */ 814 if (ReplaceIfExists && NewPathU.Length) 815 { 816 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR)); 817 if (NewBuffer == NULL) 818 { 819 BaseSetLastNTError(STATUS_NO_MEMORY); 820 _SEH2_LEAVE; 821 } 822 823 NewBuffer[0] = L'!'; 824 RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length); 825 NewPathU.Length += sizeof(WCHAR); 826 NewPathU.MaximumLength += sizeof(WCHAR); 827 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer); 828 NewPathU.Buffer = NewBuffer; 829 } 830 831 /* Check whether 'copy' renaming is allowed if required */ 832 if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED) 833 { 834 Status = STATUS_INVALID_PARAMETER; 835 } 836 else 837 { 838 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */ 839 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE); 840 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 841 { 842 /* If doesn't exist, append to first key first, creating it if it doesn't exist */ 843 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE); 844 845 if (Status == STATUS_INSUFFICIENT_RESOURCES) 846 { 847 /* If it failed because it's too big, then create 2nd key and put it there */ 848 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE); 849 } 850 } 851 } 852 853 /* If we failed at some point, return the error */ 854 if (!NT_SUCCESS(Status)) 855 { 856 BaseSetLastNTError(Status); 857 _SEH2_LEAVE; 858 } 859 860 Ret = TRUE; 861 _SEH2_LEAVE; 862 } 863 864 /* At that point, we MUST have a source handle */ 865 ASSERT(SourceHandle != INVALID_HANDLE_VALUE); 866 867 /* Allocate renaming buffer and fill it */ 868 RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION)); 869 if (RenameInfo == NULL) 870 { 871 BaseSetLastNTError(STATUS_NO_MEMORY); 872 _SEH2_LEAVE; 873 } 874 875 RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length); 876 RenameInfo->ReplaceIfExists = ReplaceIfExists; 877 RenameInfo->RootDirectory = 0; 878 RenameInfo->FileNameLength = NewPathU.Length; 879 880 /* Attempt to rename the file */ 881 Status = NtSetInformationFile(SourceHandle, 882 &IoStatusBlock, 883 RenameInfo, 884 NewPathU.Length + sizeof(FILE_RENAME_INFORMATION), 885 ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation)); 886 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo); 887 if (NT_SUCCESS(Status)) 888 { 889 /* If it succeed, all fine, quit */ 890 Ret = TRUE; 891 _SEH2_LEAVE; 892 } 893 /* If we failed for any other reason than not the same device, fail 894 * If we failed because of different devices, only allow renaming if user allowed copy 895 */ 896 if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED)) 897 { 898 /* ReactOS hack! To be removed once all FSD have proper renaming support 899 * Just leave status to error and leave 900 */ 901 if (Status == STATUS_NOT_IMPLEMENTED) 902 { 903 DPRINT1("Forcing copy, renaming not supported by FSD\n"); 904 } 905 else 906 { 907 BaseSetLastNTError(Status); 908 _SEH2_LEAVE; 909 } 910 } 911 912 /* Close source file */ 913 NtClose(SourceHandle); 914 SourceHandle = INVALID_HANDLE_VALUE; 915 916 /* Issue the copy of the file */ 917 CopyContext.Flags = dwFlags; 918 CopyContext.UserRoutine = lpProgressRoutine; 919 CopyContext.UserData = lpData; 920 NewHandle = INVALID_HANDLE_VALUE; 921 ExistingHandle = INVALID_HANDLE_VALUE; 922 923 Ret = BasepCopyFileExW(lpExistingFileName, 924 lpNewFileName, 925 BasepMoveFileCopyProgress, 926 &CopyContext, 927 NULL, 928 (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE, 929 0, 930 &ExistingHandle, 931 &NewHandle); 932 if (!Ret) 933 { 934 /* If it failed, don't leak any handle */ 935 if (ExistingHandle != INVALID_HANDLE_VALUE) 936 { 937 CloseHandle(ExistingHandle); 938 ExistingHandle = INVALID_HANDLE_VALUE; 939 } 940 } 941 else if (ExistingHandle != INVALID_HANDLE_VALUE) 942 { 943 if (NewHandle != INVALID_HANDLE_VALUE) 944 { 945 /* If copying succeed, notify */ 946 Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU); 947 if (!NT_SUCCESS(Status)) 948 { 949 /* Fail in case it had to succeed */ 950 if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE) 951 { 952 if (NewHandle != INVALID_HANDLE_VALUE) 953 CloseHandle(NewHandle); 954 NewHandle = INVALID_HANDLE_VALUE; 955 DeleteFileW(lpNewFileName); 956 Ret = FALSE; 957 BaseSetLastNTError(Status); 958 } 959 } 960 } 961 962 CloseHandle(ExistingHandle); 963 ExistingHandle = INVALID_HANDLE_VALUE; 964 } 965 966 /* In case copy worked, close file */ 967 if (NewHandle != INVALID_HANDLE_VALUE) 968 { 969 CloseHandle(NewHandle); 970 NewHandle = INVALID_HANDLE_VALUE; 971 } 972 973 /* If it succeed, delete source file */ 974 if (Ret) 975 { 976 if (!DeleteFileW(lpExistingFileName)) 977 { 978 /* Reset file attributes if required */ 979 SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL); 980 DeleteFileW(lpExistingFileName); 981 } 982 } 983 } 984 _SEH2_FINALLY 985 { 986 if (SourceHandle != INVALID_HANDLE_VALUE) 987 NtClose(SourceHandle); 988 989 RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer); 990 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer); 991 } 992 _SEH2_END; 993 994 return Ret; 995 } 996 997 998 /* 999 * @implemented 1000 */ 1001 BOOL 1002 WINAPI 1003 MoveFileWithProgressA(IN LPCSTR lpExistingFileName, 1004 IN LPCSTR lpNewFileName OPTIONAL, 1005 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, 1006 IN LPVOID lpData OPTIONAL, 1007 IN DWORD dwFlags) 1008 { 1009 BOOL Ret; 1010 UNICODE_STRING ExistingFileNameW, NewFileNameW; 1011 1012 if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName)) 1013 { 1014 return FALSE; 1015 } 1016 1017 if (lpNewFileName) 1018 { 1019 if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName)) 1020 { 1021 RtlFreeUnicodeString(&ExistingFileNameW); 1022 return FALSE; 1023 } 1024 } 1025 else 1026 { 1027 NewFileNameW.Buffer = NULL; 1028 } 1029 1030 Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags); 1031 1032 RtlFreeUnicodeString(&ExistingFileNameW); 1033 RtlFreeUnicodeString(&NewFileNameW); 1034 1035 return Ret; 1036 } 1037 1038 1039 /* 1040 * @implemented 1041 */ 1042 BOOL 1043 WINAPI 1044 MoveFileW(IN LPCWSTR lpExistingFileName, 1045 IN LPCWSTR lpNewFileName) 1046 { 1047 return MoveFileWithProgressW(lpExistingFileName, 1048 lpNewFileName, 1049 NULL, 1050 NULL, 1051 MOVEFILE_COPY_ALLOWED); 1052 } 1053 1054 1055 /* 1056 * @implemented 1057 */ 1058 BOOL 1059 WINAPI 1060 MoveFileExW(IN LPCWSTR lpExistingFileName, 1061 IN LPCWSTR lpNewFileName OPTIONAL, 1062 IN DWORD dwFlags) 1063 { 1064 return MoveFileWithProgressW(lpExistingFileName, 1065 lpNewFileName, 1066 NULL, 1067 NULL, 1068 dwFlags); 1069 } 1070 1071 1072 /* 1073 * @implemented 1074 */ 1075 BOOL 1076 WINAPI 1077 MoveFileA(IN LPCSTR lpExistingFileName, 1078 IN LPCSTR lpNewFileName) 1079 { 1080 return MoveFileWithProgressA(lpExistingFileName, 1081 lpNewFileName, 1082 NULL, 1083 NULL, 1084 MOVEFILE_COPY_ALLOWED); 1085 } 1086 1087 1088 /* 1089 * @implemented 1090 */ 1091 BOOL 1092 WINAPI 1093 MoveFileExA(IN LPCSTR lpExistingFileName, 1094 IN LPCSTR lpNewFileName OPTIONAL, 1095 IN DWORD dwFlags) 1096 { 1097 return MoveFileWithProgressA(lpExistingFileName, 1098 lpNewFileName, 1099 NULL, 1100 NULL, 1101 dwFlags); 1102 } 1103 1104 /* 1105 * @implemented 1106 */ 1107 BOOL 1108 WINAPI 1109 ReplaceFileA(IN LPCSTR lpReplacedFileName, 1110 IN LPCSTR lpReplacementFileName, 1111 IN LPCSTR lpBackupFileName OPTIONAL, 1112 IN DWORD dwReplaceFlags, 1113 IN LPVOID lpExclude, 1114 IN LPVOID lpReserved) 1115 { 1116 BOOL Ret; 1117 UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW; 1118 1119 if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS)) 1120 { 1121 SetLastError(ERROR_INVALID_PARAMETER); 1122 return FALSE; 1123 } 1124 1125 if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName)) 1126 { 1127 return FALSE; 1128 } 1129 1130 if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName)) 1131 { 1132 RtlFreeUnicodeString(&ReplacedFileNameW); 1133 return FALSE; 1134 } 1135 1136 if (lpBackupFileName) 1137 { 1138 if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName)) 1139 { 1140 RtlFreeUnicodeString(&ReplacementFileNameW); 1141 RtlFreeUnicodeString(&ReplacedFileNameW); 1142 return FALSE; 1143 } 1144 } 1145 else 1146 { 1147 BackupFileNameW.Buffer = NULL; 1148 } 1149 1150 Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0); 1151 1152 if (lpBackupFileName) 1153 { 1154 RtlFreeUnicodeString(&BackupFileNameW); 1155 } 1156 RtlFreeUnicodeString(&ReplacementFileNameW); 1157 RtlFreeUnicodeString(&ReplacedFileNameW); 1158 1159 return Ret; 1160 } 1161 1162 /* 1163 * @unimplemented 1164 */ 1165 BOOL 1166 WINAPI 1167 ReplaceFileW( 1168 LPCWSTR lpReplacedFileName, 1169 LPCWSTR lpReplacementFileName, 1170 LPCWSTR lpBackupFileName, 1171 DWORD dwReplaceFlags, 1172 LPVOID lpExclude, 1173 LPVOID lpReserved 1174 ) 1175 { 1176 HANDLE hReplaced = NULL, hReplacement = NULL; 1177 UNICODE_STRING NtReplacedName = { 0, 0, NULL }; 1178 UNICODE_STRING NtReplacementName = { 0, 0, NULL }; 1179 DWORD Error = ERROR_SUCCESS; 1180 NTSTATUS Status; 1181 BOOL Ret = FALSE; 1182 IO_STATUS_BLOCK IoStatusBlock; 1183 OBJECT_ATTRIBUTES ObjectAttributes; 1184 PVOID Buffer = NULL ; 1185 1186 if (dwReplaceFlags) 1187 FIXME("Ignoring flags %x\n", dwReplaceFlags); 1188 1189 /* First two arguments are mandatory */ 1190 if (!lpReplacedFileName || !lpReplacementFileName) 1191 { 1192 SetLastError(ERROR_INVALID_PARAMETER); 1193 return FALSE; 1194 } 1195 1196 /* Back it up */ 1197 if(lpBackupFileName) 1198 { 1199 if(!CopyFileW(lpReplacedFileName, lpBackupFileName, FALSE)) 1200 { 1201 Error = GetLastError(); 1202 goto Cleanup ; 1203 } 1204 } 1205 1206 /* Open the "replaced" file for reading and writing */ 1207 if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &NtReplacedName, NULL, NULL))) 1208 { 1209 Error = ERROR_PATH_NOT_FOUND; 1210 goto Cleanup; 1211 } 1212 1213 InitializeObjectAttributes(&ObjectAttributes, 1214 &NtReplacedName, 1215 OBJ_CASE_INSENSITIVE, 1216 NULL, 1217 NULL); 1218 1219 Status = NtOpenFile(&hReplaced, 1220 GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | WRITE_DAC, 1221 &ObjectAttributes, 1222 &IoStatusBlock, 1223 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1224 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 1225 1226 if (!NT_SUCCESS(Status)) 1227 { 1228 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 1229 Error = ERROR_FILE_NOT_FOUND; 1230 else 1231 Error = ERROR_UNABLE_TO_REMOVE_REPLACED; 1232 goto Cleanup; 1233 } 1234 1235 /* Blank it */ 1236 SetEndOfFile(hReplaced) ; 1237 1238 /* 1239 * Open the replacement file for reading, writing, and deleting 1240 * (deleting is needed when finished) 1241 */ 1242 if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &NtReplacementName, NULL, NULL))) 1243 { 1244 Error = ERROR_PATH_NOT_FOUND; 1245 goto Cleanup; 1246 } 1247 1248 InitializeObjectAttributes(&ObjectAttributes, 1249 &NtReplacementName, 1250 OBJ_CASE_INSENSITIVE, 1251 NULL, 1252 NULL); 1253 1254 Status = NtOpenFile(&hReplacement, 1255 GENERIC_READ | DELETE | SYNCHRONIZE, 1256 &ObjectAttributes, 1257 &IoStatusBlock, 1258 0, 1259 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE); 1260 1261 if (!NT_SUCCESS(Status)) 1262 { 1263 Error = RtlNtStatusToDosError(Status); 1264 goto Cleanup; 1265 } 1266 1267 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000) ; 1268 if (!Buffer) 1269 { 1270 Error = ERROR_NOT_ENOUGH_MEMORY; 1271 goto Cleanup ; 1272 } 1273 while (Status != STATUS_END_OF_FILE) 1274 { 1275 Status = NtReadFile(hReplacement, NULL, NULL, NULL, &IoStatusBlock, Buffer, 0x10000, NULL, NULL) ; 1276 if (NT_SUCCESS(Status)) 1277 { 1278 Status = NtWriteFile(hReplaced, NULL, NULL, NULL, &IoStatusBlock, Buffer, 1279 IoStatusBlock.Information, NULL, NULL) ; 1280 if (!NT_SUCCESS(Status)) 1281 { 1282 Error = RtlNtStatusToDosError(Status); 1283 goto Cleanup; 1284 } 1285 } 1286 else if (Status != STATUS_END_OF_FILE) 1287 { 1288 Error = RtlNtStatusToDosError(Status); 1289 goto Cleanup; 1290 } 1291 } 1292 1293 Ret = TRUE; 1294 1295 /* Perform resource cleanup */ 1296 Cleanup: 1297 if (hReplaced) NtClose(hReplaced); 1298 if (hReplacement) NtClose(hReplacement); 1299 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1300 1301 if (NtReplacementName.Buffer) 1302 RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName.Buffer); 1303 if (NtReplacedName.Buffer) 1304 RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName.Buffer); 1305 1306 /* If there was an error, set the error code */ 1307 if(!Ret) 1308 { 1309 TRACE("ReplaceFileW failed (error=%lu)\n", Error); 1310 SetLastError(Error); 1311 } 1312 return Ret; 1313 } 1314 1315 1316 /* 1317 * @implemented 1318 */ 1319 BOOL 1320 WINAPI 1321 PrivMoveFileIdentityW(IN LPCWSTR lpSource, IN LPCWSTR lpDestination, IN DWORD dwFlags) 1322 { 1323 ACCESS_MASK SourceAccess; 1324 UNICODE_STRING NtSource, NtDestination; 1325 LPWSTR RelativeSource, RelativeDestination; 1326 HANDLE SourceHandle, DestinationHandle; 1327 OBJECT_ATTRIBUTES ObjectAttributesSource, ObjectAttributesDestination; 1328 NTSTATUS Status, OldStatus = STATUS_SUCCESS; 1329 ACCESS_MASK DestAccess; 1330 IO_STATUS_BLOCK IoStatusBlock; 1331 FILE_BASIC_INFORMATION SourceInformation, DestinationInformation; 1332 FILE_DISPOSITION_INFORMATION FileDispositionInfo; 1333 1334 DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource, lpDestination, dwFlags); 1335 1336 SourceHandle = INVALID_HANDLE_VALUE; 1337 NtSource.Length = 1338 NtSource.MaximumLength = 0; 1339 NtSource.Buffer = NULL; 1340 RelativeSource = NULL; 1341 DestinationHandle = INVALID_HANDLE_VALUE; 1342 NtDestination.Length = 1343 NtDestination.MaximumLength = 0; 1344 NtDestination.Buffer = NULL; 1345 RelativeDestination = NULL; 1346 1347 /* FILE_WRITE_DATA is required for later on notification */ 1348 SourceAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_DATA; 1349 if (dwFlags & PRIV_DELETE_ON_SUCCESS) 1350 { 1351 SourceAccess |= DELETE; 1352 } 1353 1354 _SEH2_TRY 1355 { 1356 /* We will loop twice: 1357 * First we attempt to open with FILE_WRITE_DATA for notification 1358 * If it fails and we have flag for non-trackable files, we retry 1359 * without FILE_WRITE_DATA. 1360 * If that one fails, then, we quit for real 1361 */ 1362 while (TRUE) 1363 { 1364 Status = BasepOpenFileForMove(lpSource, 1365 &NtSource, 1366 &RelativeSource, 1367 &SourceHandle, 1368 &ObjectAttributesSource, 1369 SourceAccess, 1370 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1371 FILE_OPEN_NO_RECALL); 1372 if (NT_SUCCESS(Status)) 1373 { 1374 break; 1375 } 1376 1377 /* If we already attempted the opening without FILE_WRITE_DATA 1378 * or if we cannot move on non-trackable files, fail. 1379 */ 1380 if (!(SourceAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE)) 1381 { 1382 _SEH2_LEAVE; 1383 } 1384 1385 if (RelativeSource) 1386 { 1387 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource); 1388 RelativeSource = NULL; 1389 } 1390 1391 if (SourceHandle != INVALID_HANDLE_VALUE) 1392 { 1393 NtClose(SourceHandle); 1394 SourceHandle = INVALID_HANDLE_VALUE; 1395 } 1396 1397 SourceAccess &= ~FILE_WRITE_DATA; 1398 1399 /* Remember fist failure in the path */ 1400 if (NT_SUCCESS(OldStatus)) 1401 { 1402 OldStatus = Status; 1403 } 1404 } 1405 1406 DestAccess = FILE_WRITE_ATTRIBUTES; 1407 /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination 1408 * Still for notification purposes 1409 */ 1410 if (SourceAccess & FILE_WRITE_DATA) 1411 { 1412 DestAccess |= FILE_WRITE_DATA; 1413 } 1414 1415 /* cf comment for first loop */ 1416 while (TRUE) 1417 { 1418 Status = BasepOpenFileForMove(lpDestination, 1419 &NtDestination, 1420 &RelativeDestination, 1421 &DestinationHandle, 1422 &ObjectAttributesDestination, 1423 DestAccess, 1424 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1425 FILE_OPEN_NO_RECALL); 1426 if (NT_SUCCESS(Status)) 1427 { 1428 break; 1429 } 1430 1431 /* If we already attempted the opening without FILE_WRITE_DATA 1432 * or if we cannot move on non-trackable files, fail. 1433 */ 1434 if (!(DestAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE)) 1435 { 1436 _SEH2_LEAVE; 1437 } 1438 1439 if (RelativeDestination) 1440 { 1441 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination); 1442 RelativeDestination = NULL; 1443 } 1444 1445 if (DestinationHandle != INVALID_HANDLE_VALUE) 1446 { 1447 NtClose(DestinationHandle); 1448 DestinationHandle = INVALID_HANDLE_VALUE; 1449 } 1450 1451 DestAccess &= ~FILE_WRITE_DATA; 1452 1453 /* Remember fist failure in the path */ 1454 if (NT_SUCCESS(OldStatus)) 1455 { 1456 OldStatus = Status; 1457 } 1458 } 1459 1460 /* Get the creation time from source */ 1461 Status = NtQueryInformationFile(SourceHandle, 1462 &IoStatusBlock, 1463 &SourceInformation, 1464 sizeof(SourceInformation), 1465 FileBasicInformation); 1466 if (NT_SUCCESS(Status)) 1467 { 1468 /* Then, prepare to set it for destination */ 1469 RtlZeroMemory(&DestinationInformation, sizeof(DestinationInformation)); 1470 DestinationInformation.CreationTime.QuadPart = SourceInformation.CreationTime.QuadPart; 1471 1472 /* And set it, that's all folks! */ 1473 Status = NtSetInformationFile(DestinationHandle, 1474 &IoStatusBlock, 1475 &DestinationInformation, 1476 sizeof(DestinationInformation), 1477 FileBasicInformation); 1478 } 1479 1480 if (!NT_SUCCESS(Status)) 1481 { 1482 if (!(dwFlags & PRIV_ALLOW_NON_TRACKABLE)) 1483 { 1484 _SEH2_LEAVE; 1485 } 1486 1487 /* Remember the failure for later notification */ 1488 if (NT_SUCCESS(OldStatus)) 1489 { 1490 OldStatus = Status; 1491 } 1492 } 1493 1494 /* If we could open with FILE_WRITE_DATA both source and destination, 1495 * then, notify 1496 */ 1497 if (DestAccess & FILE_WRITE_DATA && SourceAccess & FILE_WRITE_DATA) 1498 { 1499 Status = BasepNotifyTrackingService(&SourceHandle, 1500 &ObjectAttributesSource, 1501 DestinationHandle, 1502 &NtDestination); 1503 if (!NT_SUCCESS(Status)) 1504 { 1505 if (dwFlags & PRIV_ALLOW_NON_TRACKABLE) 1506 { 1507 if (NT_SUCCESS(OldStatus)) 1508 OldStatus = Status; 1509 1510 /* Reset status, we allow non trackable files */ 1511 Status = STATUS_SUCCESS; 1512 } 1513 } 1514 } 1515 } 1516 _SEH2_FINALLY 1517 { 1518 if (RelativeSource) 1519 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource); 1520 1521 if (RelativeDestination) 1522 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination); 1523 } 1524 _SEH2_END; 1525 1526 /* If caller asked for source deletion, if everything succeed, proceed */ 1527 if (NT_SUCCESS(Status) && dwFlags & PRIV_DELETE_ON_SUCCESS) 1528 { 1529 FileDispositionInfo.DeleteFile = TRUE; 1530 1531 Status = NtSetInformationFile(SourceHandle, 1532 &IoStatusBlock, 1533 &FileDispositionInfo, 1534 sizeof(FileDispositionInfo), 1535 FileDispositionInformation); 1536 } 1537 1538 /* Cleanup/close portion */ 1539 if (DestinationHandle != INVALID_HANDLE_VALUE) 1540 { 1541 NtClose(DestinationHandle); 1542 } 1543 1544 if (SourceHandle != INVALID_HANDLE_VALUE) 1545 { 1546 NtClose(SourceHandle); 1547 } 1548 1549 /* Set last error if any, and quit */ 1550 if (NT_SUCCESS(Status)) 1551 { 1552 if (!NT_SUCCESS(OldStatus)) 1553 { 1554 BaseSetLastNTError(OldStatus); 1555 } 1556 } 1557 else 1558 { 1559 BaseSetLastNTError(Status); 1560 } 1561 1562 return NT_SUCCESS(Status); 1563 } 1564 1565 /* EOF */ 1566