1 /* 2 * PROJECT: ReactOS Setup Library 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: File support functions. 5 * COPYRIGHT: Casper S. Hornstrup (chorns@users.sourceforge.net) 6 * Copyright 2017-2018 Hermes Belusca-Maito 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include "precomp.h" 12 #include "filesup.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 18 // ACHTUNG! HAXX FIXME!! 19 #define _SEH2_TRY 20 #define _SEH2_LEAVE goto __SEH2_FINALLY__label; 21 #define _SEH2_FINALLY __SEH2_FINALLY__label: 22 #define _SEH2_END 23 24 25 /* FUNCTIONS ****************************************************************/ 26 27 static 28 NTSTATUS 29 SetupCreateSingleDirectory( 30 _In_ PCUNICODE_STRING DirectoryName) 31 { 32 NTSTATUS Status; 33 UNICODE_STRING PathName = *DirectoryName; 34 OBJECT_ATTRIBUTES ObjectAttributes; 35 IO_STATUS_BLOCK IoStatusBlock; 36 HANDLE DirectoryHandle; 37 38 /* Remove the trailing separator if needed */ 39 if (PathName.Length >= 2 * sizeof(WCHAR) && 40 PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == OBJ_NAME_PATH_SEPARATOR) 41 { 42 PathName.Length -= sizeof(WCHAR); 43 } 44 45 InitializeObjectAttributes(&ObjectAttributes, 46 &PathName, 47 OBJ_CASE_INSENSITIVE, 48 NULL, 49 NULL); 50 51 Status = NtCreateFile(&DirectoryHandle, 52 FILE_LIST_DIRECTORY | SYNCHRONIZE, 53 &ObjectAttributes, 54 &IoStatusBlock, 55 NULL, 56 FILE_ATTRIBUTE_DIRECTORY, 57 FILE_SHARE_READ | FILE_SHARE_WRITE, 58 FILE_OPEN_IF, 59 FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE, 60 NULL, 61 0); 62 if (NT_SUCCESS(Status)) 63 NtClose(DirectoryHandle); 64 65 return Status; 66 } 67 68 /** 69 * @brief 70 * Create a new directory, specified by the given path. 71 * Any intermediate non-existing directory is created as well. 72 * 73 * @param[in] PathName 74 * The path of the directory to be created. 75 * 76 * @return An NTSTATUS code indicating success or failure. 77 **/ 78 NTSTATUS 79 SetupCreateDirectory( 80 _In_ PCWSTR PathName) 81 { 82 NTSTATUS Status = STATUS_SUCCESS; 83 UNICODE_STRING PathNameU; 84 PCWSTR Buffer; 85 PCWCH Ptr, End; 86 87 RtlInitUnicodeString(&PathNameU, PathName); 88 Buffer = PathNameU.Buffer; 89 End = Buffer + (PathNameU.Length / sizeof(WCHAR)); 90 91 /* Find the deepest existing sub-directory: start from the 92 * end and go back, verifying each sub-directory in turn */ 93 for (Ptr = End; Ptr > Buffer;) 94 { 95 BOOLEAN bExists; 96 97 /* If we are on a separator, truncate at the next character. 98 * The trailing separator is kept for the existence check. */ 99 if ((Ptr < End) && (*Ptr == OBJ_NAME_PATH_SEPARATOR)) 100 PathNameU.Length = (ULONG_PTR)(Ptr+1) - (ULONG_PTR)Buffer; 101 102 /* Check if the sub-directory exists and stop 103 * if so: this is the deepest existing one */ 104 DPRINT("PathName: %wZ\n", &PathNameU); 105 bExists = DoesPathExist_UStr(NULL, &PathNameU, TRUE); 106 if (bExists) 107 break; 108 109 /* Skip back any consecutive path separators */ 110 while ((Ptr > Buffer) && (*Ptr == OBJ_NAME_PATH_SEPARATOR)) 111 --Ptr; 112 /* Go to the beginning of the path component, stop at the separator */ 113 while ((Ptr > Buffer) && (*Ptr != OBJ_NAME_PATH_SEPARATOR)) 114 --Ptr; 115 } 116 117 /* Skip any consecutive path separators */ 118 while ((Ptr < End) && (*Ptr == OBJ_NAME_PATH_SEPARATOR)) 119 ++Ptr; 120 121 /* Create all the remaining sub-directories */ 122 for (; Ptr < End; ++Ptr) 123 { 124 /* Go to the end of the current path component, stop at 125 * the separator or terminating NUL and truncate it */ 126 while ((Ptr < End) && (*Ptr != OBJ_NAME_PATH_SEPARATOR)) 127 ++Ptr; 128 PathNameU.Length = (ULONG_PTR)Ptr - (ULONG_PTR)Buffer; 129 130 DPRINT("Create: %wZ\n", &PathNameU); 131 Status = SetupCreateSingleDirectory(&PathNameU); 132 if (!NT_SUCCESS(Status)) 133 break; 134 } 135 136 DPRINT("Done.\n"); 137 return Status; 138 } 139 140 NTSTATUS 141 SetupDeleteFile( 142 IN PCWSTR FileName, 143 IN BOOLEAN ForceDelete) // ForceDelete can be used to delete read-only files 144 { 145 NTSTATUS Status; 146 UNICODE_STRING NtPathU; 147 OBJECT_ATTRIBUTES ObjectAttributes; 148 IO_STATUS_BLOCK IoStatusBlock; 149 HANDLE FileHandle; 150 FILE_DISPOSITION_INFORMATION FileDispInfo; 151 BOOLEAN RetryOnce = FALSE; 152 153 /* Open the directory name that was passed in */ 154 RtlInitUnicodeString(&NtPathU, FileName); 155 InitializeObjectAttributes(&ObjectAttributes, 156 &NtPathU, 157 OBJ_CASE_INSENSITIVE, 158 NULL, 159 NULL); 160 161 Retry: // We go back there once if RetryOnce == TRUE 162 Status = NtOpenFile(&FileHandle, 163 DELETE | FILE_READ_ATTRIBUTES | 164 (RetryOnce ? FILE_WRITE_ATTRIBUTES : 0), 165 &ObjectAttributes, 166 &IoStatusBlock, 167 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 168 FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); 169 if (!NT_SUCCESS(Status)) 170 { 171 DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status); 172 return Status; 173 } 174 175 if (RetryOnce) 176 { 177 FILE_BASIC_INFORMATION FileInformation; 178 179 Status = NtQueryInformationFile(FileHandle, 180 &IoStatusBlock, 181 &FileInformation, 182 sizeof(FILE_BASIC_INFORMATION), 183 FileBasicInformation); 184 if (!NT_SUCCESS(Status)) 185 { 186 DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status); 187 NtClose(FileHandle); 188 return Status; 189 } 190 191 FileInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL; 192 Status = NtSetInformationFile(FileHandle, 193 &IoStatusBlock, 194 &FileInformation, 195 sizeof(FILE_BASIC_INFORMATION), 196 FileBasicInformation); 197 NtClose(FileHandle); 198 if (!NT_SUCCESS(Status)) 199 { 200 DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status); 201 return Status; 202 } 203 } 204 205 /* Ask for the file to be deleted */ 206 FileDispInfo.DeleteFile = TRUE; 207 Status = NtSetInformationFile(FileHandle, 208 &IoStatusBlock, 209 &FileDispInfo, 210 sizeof(FILE_DISPOSITION_INFORMATION), 211 FileDispositionInformation); 212 NtClose(FileHandle); 213 214 if (!NT_SUCCESS(Status)) 215 DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName, Status); 216 217 // FIXME: Check the precise value of Status! 218 if (!NT_SUCCESS(Status) && ForceDelete && !RetryOnce) 219 { 220 /* Retry once */ 221 RetryOnce = TRUE; 222 goto Retry; 223 } 224 225 /* Return result to the caller */ 226 return Status; 227 } 228 229 NTSTATUS 230 SetupCopyFile( 231 IN PCWSTR SourceFileName, 232 IN PCWSTR DestinationFileName, 233 IN BOOLEAN FailIfExists) 234 { 235 NTSTATUS Status; 236 UNICODE_STRING FileName; 237 OBJECT_ATTRIBUTES ObjectAttributes; 238 HANDLE FileHandleSource; 239 HANDLE FileHandleDest; 240 IO_STATUS_BLOCK IoStatusBlock; 241 FILE_STANDARD_INFORMATION FileStandard; 242 FILE_BASIC_INFORMATION FileBasic; 243 ULONG RegionSize; 244 HANDLE SourceFileSection; 245 PVOID SourceFileMap = NULL; 246 SIZE_T SourceSectionSize = 0; 247 LARGE_INTEGER ByteOffset; 248 249 RtlInitUnicodeString(&FileName, SourceFileName); 250 InitializeObjectAttributes(&ObjectAttributes, 251 &FileName, 252 OBJ_CASE_INSENSITIVE, 253 NULL, 254 NULL); 255 256 Status = NtOpenFile(&FileHandleSource, 257 GENERIC_READ, 258 &ObjectAttributes, 259 &IoStatusBlock, 260 FILE_SHARE_READ, 261 FILE_SEQUENTIAL_ONLY); 262 if (!NT_SUCCESS(Status)) 263 { 264 DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName); 265 goto done; 266 } 267 268 Status = NtQueryInformationFile(FileHandleSource, 269 &IoStatusBlock, 270 &FileStandard, 271 sizeof(FILE_STANDARD_INFORMATION), 272 FileStandardInformation); 273 if (!NT_SUCCESS(Status)) 274 { 275 DPRINT1("NtQueryInformationFile failed: %x\n", Status); 276 goto closesrc; 277 } 278 279 Status = NtQueryInformationFile(FileHandleSource, 280 &IoStatusBlock, 281 &FileBasic, 282 sizeof(FILE_BASIC_INFORMATION), 283 FileBasicInformation); 284 if (!NT_SUCCESS(Status)) 285 { 286 DPRINT1("NtQueryInformationFile failed: %x\n", Status); 287 goto closesrc; 288 } 289 290 Status = NtCreateSection(&SourceFileSection, 291 SECTION_MAP_READ, 292 NULL, 293 NULL, 294 PAGE_READONLY, 295 SEC_COMMIT, 296 FileHandleSource); 297 if (!NT_SUCCESS(Status)) 298 { 299 DPRINT1("NtCreateSection failed: %x, %S\n", Status, SourceFileName); 300 goto closesrc; 301 } 302 303 Status = NtMapViewOfSection(SourceFileSection, 304 NtCurrentProcess(), 305 &SourceFileMap, 306 0, 307 0, 308 NULL, 309 &SourceSectionSize, 310 ViewUnmap, 311 0, 312 PAGE_READONLY); 313 if (!NT_SUCCESS(Status)) 314 { 315 DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName); 316 goto closesrcsec; 317 } 318 319 RtlInitUnicodeString(&FileName, DestinationFileName); 320 InitializeObjectAttributes(&ObjectAttributes, 321 &FileName, 322 OBJ_CASE_INSENSITIVE, 323 NULL, 324 NULL); 325 326 Status = NtCreateFile(&FileHandleDest, 327 GENERIC_WRITE | SYNCHRONIZE, 328 &ObjectAttributes, 329 &IoStatusBlock, 330 NULL, 331 FileBasic.FileAttributes, // FILE_ATTRIBUTE_NORMAL, 332 0, 333 FailIfExists ? FILE_CREATE : FILE_OVERWRITE_IF, 334 FILE_NO_INTERMEDIATE_BUFFERING | 335 FILE_SEQUENTIAL_ONLY | 336 FILE_SYNCHRONOUS_IO_NONALERT, 337 NULL, 338 0); 339 if (!NT_SUCCESS(Status)) 340 { 341 /* 342 * Open may have failed because the file to overwrite 343 * is in readonly mode. 344 */ 345 if (Status == STATUS_ACCESS_DENIED) 346 { 347 FILE_BASIC_INFORMATION FileBasicInfo; 348 349 /* Reattempt to open it with limited access */ 350 Status = NtCreateFile(&FileHandleDest, 351 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, 352 &ObjectAttributes, 353 &IoStatusBlock, 354 NULL, 355 FILE_ATTRIBUTE_NORMAL, 356 0, 357 FILE_OPEN, 358 FILE_NO_INTERMEDIATE_BUFFERING | 359 FILE_SEQUENTIAL_ONLY | 360 FILE_SYNCHRONOUS_IO_NONALERT, 361 NULL, 362 0); 363 /* Fail for real if we cannot open it that way */ 364 if (!NT_SUCCESS(Status)) 365 { 366 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName); 367 goto unmapsrcsec; 368 } 369 370 /* Zero our basic info, just to set attributes */ 371 RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo)); 372 /* Reset attributes to normal, no read-only */ 373 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; 374 /* 375 * We basically don't care about whether it succeed: 376 * if it didn't, later open will fail. 377 */ 378 NtSetInformationFile(FileHandleDest, &IoStatusBlock, &FileBasicInfo, 379 sizeof(FileBasicInfo), FileBasicInformation); 380 381 /* Close file */ 382 NtClose(FileHandleDest); 383 384 /* And re-attempt overwrite */ 385 Status = NtCreateFile(&FileHandleDest, 386 GENERIC_WRITE | SYNCHRONIZE, 387 &ObjectAttributes, 388 &IoStatusBlock, 389 NULL, 390 FILE_ATTRIBUTE_NORMAL, 391 0, 392 FILE_OVERWRITE_IF, 393 FILE_NO_INTERMEDIATE_BUFFERING | 394 FILE_SEQUENTIAL_ONLY | 395 FILE_SYNCHRONOUS_IO_NONALERT, 396 NULL, 397 0); 398 } 399 400 /* We failed */ 401 if (!NT_SUCCESS(Status)) 402 { 403 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName); 404 goto unmapsrcsec; 405 } 406 } 407 408 RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart); 409 IoStatusBlock.Status = 0; 410 ByteOffset.QuadPart = 0ULL; 411 Status = NtWriteFile(FileHandleDest, 412 NULL, 413 NULL, 414 NULL, 415 &IoStatusBlock, 416 SourceFileMap, 417 RegionSize, 418 &ByteOffset, 419 NULL); 420 if (!NT_SUCCESS(Status)) 421 { 422 DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", 423 Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize); 424 goto closedest; 425 } 426 427 /* Copy file date/time from source file */ 428 Status = NtSetInformationFile(FileHandleDest, 429 &IoStatusBlock, 430 &FileBasic, 431 sizeof(FILE_BASIC_INFORMATION), 432 FileBasicInformation); 433 if (!NT_SUCCESS(Status)) 434 { 435 DPRINT1("NtSetInformationFile failed: %x\n", Status); 436 goto closedest; 437 } 438 439 /* Shorten the file back to its real size after completing the write */ 440 Status = NtSetInformationFile(FileHandleDest, 441 &IoStatusBlock, 442 &FileStandard.EndOfFile, 443 sizeof(FILE_END_OF_FILE_INFORMATION), 444 FileEndOfFileInformation); 445 if (!NT_SUCCESS(Status)) 446 { 447 DPRINT1("NtSetInformationFile failed: %x\n", Status); 448 } 449 450 closedest: 451 NtClose(FileHandleDest); 452 453 unmapsrcsec: 454 NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap); 455 456 closesrcsec: 457 NtClose(SourceFileSection); 458 459 closesrc: 460 NtClose(FileHandleSource); 461 462 done: 463 return Status; 464 } 465 466 /* 467 * Synchronized with its kernel32 counterpart, but we don't manage reparse points here. 468 */ 469 NTSTATUS 470 SetupMoveFile( 471 IN PCWSTR ExistingFileName, 472 IN PCWSTR NewFileName, 473 IN ULONG Flags) 474 { 475 NTSTATUS Status; 476 IO_STATUS_BLOCK IoStatusBlock; 477 OBJECT_ATTRIBUTES ObjectAttributes; 478 PFILE_RENAME_INFORMATION RenameInfo; 479 UNICODE_STRING NewPathU, ExistingPathU; 480 HANDLE SourceHandle = NULL; 481 BOOLEAN ReplaceIfExists; 482 483 RtlInitUnicodeString(&ExistingPathU, ExistingFileName); 484 RtlInitUnicodeString(&NewPathU, NewFileName); 485 486 _SEH2_TRY 487 { 488 ReplaceIfExists = !!(Flags & MOVEFILE_REPLACE_EXISTING); 489 490 /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */ 491 InitializeObjectAttributes(&ObjectAttributes, 492 &ExistingPathU, 493 OBJ_CASE_INSENSITIVE, 494 NULL, 495 NULL); 496 /* Attempt to open source file */ 497 Status = NtOpenFile(&SourceHandle, 498 FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, 499 &ObjectAttributes, 500 &IoStatusBlock, 501 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 502 FILE_OPEN_FOR_BACKUP_INTENT | ((Flags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0)); 503 if (!NT_SUCCESS(Status)) 504 { 505 if (Status != STATUS_INVALID_PARAMETER) 506 { 507 _SEH2_LEAVE; 508 } 509 } 510 511 /* At that point, we MUST have a source handle */ 512 ASSERT(SourceHandle); 513 514 /* Allocate renaming buffer and fill it */ 515 RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION)); 516 if (RenameInfo == NULL) 517 { 518 Status = STATUS_NO_MEMORY; 519 _SEH2_LEAVE; 520 } 521 522 RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length); 523 RenameInfo->ReplaceIfExists = ReplaceIfExists; 524 RenameInfo->RootDirectory = NULL; 525 RenameInfo->FileNameLength = NewPathU.Length; 526 527 /* Attempt to rename the file */ 528 Status = NtSetInformationFile(SourceHandle, 529 &IoStatusBlock, 530 RenameInfo, 531 NewPathU.Length + sizeof(FILE_RENAME_INFORMATION), 532 FileRenameInformation); 533 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo); 534 if (NT_SUCCESS(Status)) 535 { 536 /* If it succeeded, all fine, quit */ 537 _SEH2_LEAVE; 538 } 539 /* 540 * If we failed for any other reason than not the same device, fail. 541 * If we failed because of different devices, only allow renaming 542 * if user allowed copy. 543 */ 544 if (Status != STATUS_NOT_SAME_DEVICE || !(Flags & MOVEFILE_COPY_ALLOWED)) 545 { 546 /* ReactOS hack! To be removed once all FSD have proper renaming support 547 * Just leave status to error and leave 548 */ 549 if (Status == STATUS_NOT_IMPLEMENTED) 550 { 551 DPRINT1("Forcing copy, renaming not supported by FSD\n"); 552 } 553 else 554 { 555 _SEH2_LEAVE; 556 } 557 } 558 559 /* Close the source file */ 560 NtClose(SourceHandle); 561 SourceHandle = NULL; 562 563 /* Perform the file copy */ 564 Status = SetupCopyFile(ExistingFileName, 565 NewFileName, 566 !ReplaceIfExists); 567 568 /* If it succeeded, delete the source file */ 569 if (NT_SUCCESS(Status)) 570 { 571 /* Force-delete files even if read-only */ 572 SetupDeleteFile(ExistingFileName, TRUE); 573 } 574 } 575 _SEH2_FINALLY 576 { 577 if (SourceHandle) 578 NtClose(SourceHandle); 579 } 580 _SEH2_END; 581 582 return Status; 583 } 584 585 NTSTATUS 586 ConcatPathsV( 587 IN OUT PWSTR PathBuffer, 588 IN SIZE_T cchPathSize, 589 IN ULONG NumberOfPathComponents, 590 IN va_list PathComponentsList) 591 { 592 NTSTATUS Status = STATUS_SUCCESS; 593 SIZE_T cchPathLen; 594 PCWSTR PathComponent; 595 596 if (cchPathSize < 1) 597 return STATUS_SUCCESS; 598 599 while (NumberOfPathComponents--) 600 { 601 PathComponent = va_arg(PathComponentsList, PCWSTR); 602 if (!PathComponent) 603 continue; 604 605 cchPathLen = min(cchPathSize, wcslen(PathBuffer)); 606 if (cchPathLen >= cchPathSize) 607 return STATUS_BUFFER_OVERFLOW; 608 609 if (PathComponent[0] != OBJ_NAME_PATH_SEPARATOR && 610 cchPathLen > 0 && PathBuffer[cchPathLen-1] != OBJ_NAME_PATH_SEPARATOR) 611 { 612 /* PathComponent does not start with '\' and PathBuffer does not end with '\' */ 613 Status = RtlStringCchCatW(PathBuffer, cchPathSize, L"\\"); 614 if (!NT_SUCCESS(Status)) 615 return Status; 616 } 617 else if (PathComponent[0] == OBJ_NAME_PATH_SEPARATOR && 618 cchPathLen > 0 && PathBuffer[cchPathLen-1] == OBJ_NAME_PATH_SEPARATOR) 619 { 620 /* PathComponent starts with '\' and PathBuffer ends with '\' */ 621 while (*PathComponent == OBJ_NAME_PATH_SEPARATOR) 622 ++PathComponent; // Skip any backslash 623 } 624 Status = RtlStringCchCatW(PathBuffer, cchPathSize, PathComponent); 625 if (!NT_SUCCESS(Status)) 626 return Status; 627 } 628 629 return Status; 630 } 631 632 NTSTATUS 633 CombinePathsV( 634 OUT PWSTR PathBuffer, 635 IN SIZE_T cchPathSize, 636 IN ULONG NumberOfPathComponents, 637 IN va_list PathComponentsList) 638 { 639 if (cchPathSize < 1) 640 return STATUS_SUCCESS; 641 642 *PathBuffer = UNICODE_NULL; 643 return ConcatPathsV(PathBuffer, cchPathSize, 644 NumberOfPathComponents, 645 PathComponentsList); 646 } 647 648 NTSTATUS 649 ConcatPaths( 650 IN OUT PWSTR PathBuffer, 651 IN SIZE_T cchPathSize, 652 IN ULONG NumberOfPathComponents, 653 IN /* PCWSTR */ ...) 654 { 655 NTSTATUS Status; 656 va_list PathComponentsList; 657 658 if (cchPathSize < 1) 659 return STATUS_SUCCESS; 660 661 va_start(PathComponentsList, NumberOfPathComponents); 662 Status = ConcatPathsV(PathBuffer, cchPathSize, 663 NumberOfPathComponents, 664 PathComponentsList); 665 va_end(PathComponentsList); 666 667 return Status; 668 } 669 670 NTSTATUS 671 CombinePaths( 672 OUT PWSTR PathBuffer, 673 IN SIZE_T cchPathSize, 674 IN ULONG NumberOfPathComponents, 675 IN /* PCWSTR */ ...) 676 { 677 NTSTATUS Status; 678 va_list PathComponentsList; 679 680 if (cchPathSize < 1) 681 return STATUS_SUCCESS; 682 683 *PathBuffer = UNICODE_NULL; 684 685 va_start(PathComponentsList, NumberOfPathComponents); 686 Status = CombinePathsV(PathBuffer, cchPathSize, 687 NumberOfPathComponents, 688 PathComponentsList); 689 va_end(PathComponentsList); 690 691 return Status; 692 } 693 694 BOOLEAN 695 DoesPathExist_UStr( 696 _In_opt_ HANDLE RootDirectory, 697 _In_ PCUNICODE_STRING PathName, 698 _In_ BOOLEAN IsDirectory) 699 { 700 NTSTATUS Status; 701 HANDLE FileHandle; 702 OBJECT_ATTRIBUTES ObjectAttributes; 703 IO_STATUS_BLOCK IoStatusBlock; 704 705 InitializeObjectAttributes(&ObjectAttributes, 706 (PUNICODE_STRING)PathName, 707 OBJ_CASE_INSENSITIVE, 708 RootDirectory, 709 NULL); 710 711 Status = NtOpenFile(&FileHandle, 712 IsDirectory ? (FILE_LIST_DIRECTORY | SYNCHRONIZE) 713 : FILE_GENERIC_READ, // Contains SYNCHRONIZE 714 &ObjectAttributes, 715 &IoStatusBlock, 716 FILE_SHARE_READ | FILE_SHARE_WRITE, 717 FILE_SYNCHRONOUS_IO_NONALERT | 718 (IsDirectory ? FILE_DIRECTORY_FILE 719 : FILE_NON_DIRECTORY_FILE)); 720 if (NT_SUCCESS(Status)) 721 { 722 NtClose(FileHandle); 723 } 724 else 725 { 726 DPRINT("Failed to open %s '%wZ', Status 0x%08lx\n", 727 IsDirectory ? "directory" : "file", 728 PathName, Status); 729 } 730 731 return NT_SUCCESS(Status); 732 } 733 734 BOOLEAN 735 DoesPathExist( 736 _In_opt_ HANDLE RootDirectory, 737 _In_ PCWSTR PathName, 738 _In_ BOOLEAN IsDirectory) 739 { 740 UNICODE_STRING PathNameU; 741 RtlInitUnicodeString(&PathNameU, PathName); 742 return DoesPathExist_UStr(RootDirectory, &PathNameU, IsDirectory); 743 } 744 745 // FIXME: DEPRECATED! HACKish function that needs to be deprecated! 746 BOOLEAN 747 DoesFileExist_2( 748 IN PCWSTR PathName OPTIONAL, 749 IN PCWSTR FileName) 750 { 751 WCHAR FullName[MAX_PATH]; 752 CombinePaths(FullName, ARRAYSIZE(FullName), 2, PathName, FileName); 753 return DoesFileExist(NULL, FullName); 754 } 755 756 /* 757 * The format of NtPath should be: 758 * \Device\HarddiskXXX\PartitionYYY[\path] , 759 * where XXX and YYY respectively represent the hard disk and partition numbers, 760 * and [\path] represent an optional path (separated by '\\'). 761 * 762 * If a NT path of such a form is correctly parsed, the function returns respectively: 763 * - in pDiskNumber: the hard disk number XXX, 764 * - in pPartNumber: the partition number YYY, 765 * - in PathComponent: pointer value (inside NtPath) to the beginning of \path. 766 * 767 * NOTE: The function does not accept leading whitespace. 768 */ 769 BOOLEAN 770 NtPathToDiskPartComponents( 771 IN PCWSTR NtPath, 772 OUT PULONG pDiskNumber, 773 OUT PULONG pPartNumber, 774 OUT PCWSTR* PathComponent OPTIONAL) 775 { 776 ULONG DiskNumber, PartNumber; 777 PCWSTR Path; 778 779 *pDiskNumber = 0; 780 *pPartNumber = 0; 781 if (PathComponent) *PathComponent = NULL; 782 783 Path = NtPath; 784 785 if (_wcsnicmp(Path, L"\\Device\\Harddisk", 16) != 0) 786 { 787 /* The NT path doesn't start with the prefix string, thus it cannot be a hard disk device path */ 788 DPRINT1("'%S' : Not a possible hard disk device.\n", NtPath); 789 return FALSE; 790 } 791 792 Path += 16; 793 794 /* A number must be present now */ 795 if (!iswdigit(*Path)) 796 { 797 DPRINT1("'%S' : expected a number! Not a regular hard disk device.\n", Path); 798 return FALSE; 799 } 800 DiskNumber = wcstoul(Path, (PWSTR*)&Path, 10); 801 802 /* Either NULL termination, or a path separator must be present now */ 803 if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR) 804 { 805 DPRINT1("'%S' : expected a path separator!\n", Path); 806 return FALSE; 807 } 808 809 if (!*Path) 810 { 811 DPRINT1("The path only specified a hard disk (and nothing else, like a partition...), so we stop there.\n"); 812 goto Quit; 813 } 814 815 /* Here, *Path == L'\\' */ 816 817 if (_wcsnicmp(Path, L"\\Partition", 10) != 0) 818 { 819 /* Actually, \Partition is optional so, if we don't have it, we still return success. Or should we? */ 820 DPRINT1("'%S' : unexpected format!\n", NtPath); 821 goto Quit; 822 } 823 824 Path += 10; 825 826 /* A number must be present now */ 827 if (!iswdigit(*Path)) 828 { 829 /* If we don't have a number it means this part of path is actually not a partition specifier, so we shouldn't fail either. Or should we? */ 830 DPRINT1("'%S' : expected a number!\n", Path); 831 goto Quit; 832 } 833 PartNumber = wcstoul(Path, (PWSTR*)&Path, 10); 834 835 /* Either NULL termination, or a path separator must be present now */ 836 if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR) 837 { 838 /* We shouldn't fail here because it just means this part of path is actually not a partition specifier. Or should we? */ 839 DPRINT1("'%S' : expected a path separator!\n", Path); 840 goto Quit; 841 } 842 843 /* OK, here we really have a partition specifier: return its number */ 844 *pPartNumber = PartNumber; 845 846 Quit: 847 /* Return the disk number */ 848 *pDiskNumber = DiskNumber; 849 850 /* Return the path component also, if the user wants it */ 851 if (PathComponent) *PathComponent = Path; 852 853 return TRUE; 854 } 855 856 /** 857 * @brief 858 * Opens and maps a file in memory. 859 * 860 * @param[in] RootDirectory 861 * @param[in] PathNameToFile 862 * Path to the file, either in absolute form, or relative to the opened 863 * root directory given by the RootDirectory handle. 864 * 865 * @param[out] FileHandle 866 * An optional pointer to a variable receiving a handle to the opened file. 867 * If NULL, the underlying file handle is closed. 868 * 869 * @param[out] FileSize 870 * An optional pointer to a variable receiving the size of the opened file. 871 * 872 * @param[out] SectionHandle 873 * A pointer to a variable receiving a handle to a section mapping the file. 874 * 875 * @param[out] BaseAddress 876 * A pointer to a variable receiving the address where the file is mapped. 877 * 878 * @param[in] ReadWriteAccess 879 * A boolean variable specifying whether to map the file for read and write 880 * access (TRUE), or read-only access (FALSE). 881 * 882 * @return 883 * STATUS_SUCCESS in case of success, or a status code in case of error. 884 **/ 885 NTSTATUS 886 OpenAndMapFile( 887 _In_opt_ HANDLE RootDirectory, 888 _In_ PCWSTR PathNameToFile, 889 _Out_opt_ PHANDLE FileHandle, 890 _Out_opt_ PULONG FileSize, 891 _Out_ PHANDLE SectionHandle, 892 _Out_ PVOID* BaseAddress, 893 _In_ BOOLEAN ReadWriteAccess) 894 { 895 NTSTATUS Status; 896 UNICODE_STRING FileName; 897 OBJECT_ATTRIBUTES ObjectAttributes; 898 IO_STATUS_BLOCK IoStatusBlock; 899 HANDLE LocalFileHandle; 900 901 /* Open the file */ 902 RtlInitUnicodeString(&FileName, PathNameToFile); 903 InitializeObjectAttributes(&ObjectAttributes, 904 &FileName, 905 OBJ_CASE_INSENSITIVE, 906 RootDirectory, 907 NULL); 908 909 if (FileHandle) *FileHandle = NULL; 910 Status = NtOpenFile(&LocalFileHandle, 911 FILE_GENERIC_READ | // Contains SYNCHRONIZE 912 (ReadWriteAccess ? FILE_GENERIC_WRITE : 0), 913 &ObjectAttributes, 914 &IoStatusBlock, 915 FILE_SHARE_READ, 916 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 917 if (!NT_SUCCESS(Status)) 918 { 919 DPRINT1("Failed to open file '%wZ' (Status 0x%08lx)\n", &FileName, Status); 920 return Status; 921 } 922 923 if (FileSize) 924 { 925 /* Query the file size */ 926 FILE_STANDARD_INFORMATION FileInfo; 927 Status = NtQueryInformationFile(LocalFileHandle, 928 &IoStatusBlock, 929 &FileInfo, 930 sizeof(FileInfo), 931 FileStandardInformation); 932 if (!NT_SUCCESS(Status)) 933 { 934 DPRINT("NtQueryInformationFile() failed (Status 0x%08lx)\n", Status); 935 goto Quit; 936 } 937 938 if (FileInfo.EndOfFile.HighPart != 0) 939 DPRINT1("WARNING!! The file '%wZ' is too large!\n", &FileName); 940 941 *FileSize = FileInfo.EndOfFile.LowPart; 942 DPRINT("File size: %lu\n", *FileSize); 943 } 944 945 /* Map the whole file into memory */ 946 Status = MapFile(LocalFileHandle, 947 SectionHandle, 948 BaseAddress, 949 ReadWriteAccess); 950 if (!NT_SUCCESS(Status)) 951 { 952 DPRINT1("Failed to map file '%wZ' (Status 0x%08lx)\n", &FileName, Status); 953 goto Quit; 954 } 955 956 Quit: 957 /* If we succeeded, return the opened file handle if needed. 958 * If we failed or the caller does not need the handle, close it now. */ 959 if (NT_SUCCESS(Status) && FileHandle) 960 *FileHandle = LocalFileHandle; 961 else 962 NtClose(LocalFileHandle); 963 964 return Status; 965 } 966 967 /** 968 * @brief 969 * Maps an opened file in memory. 970 * 971 * @param[in] FileHandle 972 * A handle to an opened file to map. 973 * 974 * @param[out] SectionHandle 975 * A pointer to a variable receiving a handle to a section mapping the file. 976 * 977 * @param[out] BaseAddress 978 * A pointer to a variable receiving the address where the file is mapped. 979 * 980 * @param[in] ReadWriteAccess 981 * A boolean variable specifying whether to map the file for read and write 982 * access (TRUE), or read-only access (FALSE). 983 * 984 * @return 985 * STATUS_SUCCESS in case of success, or a status code in case of error. 986 **/ 987 NTSTATUS 988 MapFile( 989 _In_ HANDLE FileHandle, 990 _Out_ PHANDLE SectionHandle, 991 _Out_ PVOID* BaseAddress, 992 _In_ BOOLEAN ReadWriteAccess) 993 { 994 NTSTATUS Status; 995 ULONG SectionPageProtection; 996 SIZE_T ViewSize; 997 PVOID ViewBase; 998 999 SectionPageProtection = (ReadWriteAccess ? PAGE_READWRITE : PAGE_READONLY); 1000 1001 /* Create the section */ 1002 *SectionHandle = NULL; 1003 Status = NtCreateSection(SectionHandle, 1004 STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | 1005 SECTION_MAP_READ | 1006 (ReadWriteAccess ? SECTION_MAP_WRITE : 0), 1007 NULL, 1008 NULL, 1009 SectionPageProtection, 1010 SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */, 1011 FileHandle); 1012 if (!NT_SUCCESS(Status)) 1013 { 1014 DPRINT1("Failed to create a memory section for file 0x%p (Status 0x%08lx)\n", 1015 FileHandle, Status); 1016 return Status; 1017 } 1018 1019 /* Map the section */ 1020 ViewSize = 0; 1021 ViewBase = NULL; 1022 Status = NtMapViewOfSection(*SectionHandle, 1023 NtCurrentProcess(), 1024 &ViewBase, 1025 0, 0, 1026 NULL, 1027 &ViewSize, 1028 ViewShare, 1029 0, 1030 SectionPageProtection); 1031 if (!NT_SUCCESS(Status)) 1032 { 1033 DPRINT1("Failed to map a view for file 0x%p (Status 0x%08lx)\n", 1034 FileHandle, Status); 1035 NtClose(*SectionHandle); 1036 *SectionHandle = NULL; 1037 return Status; 1038 } 1039 1040 *BaseAddress = ViewBase; 1041 return STATUS_SUCCESS; 1042 } 1043 1044 /** 1045 * @brief 1046 * Unmaps a mapped file by section. 1047 * 1048 * @param[in] SectionHandle 1049 * The handle to the section mapping the file. 1050 * 1051 * @param[in] BaseAddress 1052 * The base address where the file is mapped. 1053 * 1054 * @return 1055 * TRUE if the file was successfully unmapped; FALSE if an error occurred. 1056 **/ 1057 BOOLEAN 1058 UnMapFile( 1059 _In_ HANDLE SectionHandle, 1060 _In_ PVOID BaseAddress) 1061 { 1062 NTSTATUS Status; 1063 BOOLEAN Success = TRUE; 1064 1065 Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress); 1066 if (!NT_SUCCESS(Status)) 1067 { 1068 DPRINT1("NtUnmapViewOfSection(0x%p) failed (Status 0x%08lx)\n", 1069 BaseAddress, Status); 1070 Success = FALSE; 1071 } 1072 Status = NtClose(SectionHandle); 1073 if (!NT_SUCCESS(Status)) 1074 { 1075 DPRINT1("NtClose(0x%p) failed (Status 0x%08lx)\n", 1076 SectionHandle, Status); 1077 Success = FALSE; 1078 } 1079 1080 return Success; 1081 } 1082 1083 /* EOF */ 1084