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