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