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 NTSTATUS 858 OpenAndMapFile( 859 IN HANDLE RootDirectory OPTIONAL, 860 IN PCWSTR PathNameToFile, 861 OUT PHANDLE FileHandle, // IN OUT PHANDLE OPTIONAL 862 OUT PHANDLE SectionHandle, 863 OUT PVOID* BaseAddress, 864 OUT PULONG FileSize OPTIONAL, 865 IN BOOLEAN ReadWriteAccess) 866 { 867 NTSTATUS Status; 868 UNICODE_STRING FileName; 869 OBJECT_ATTRIBUTES ObjectAttributes; 870 IO_STATUS_BLOCK IoStatusBlock; 871 ULONG SectionPageProtection; 872 SIZE_T ViewSize; 873 PVOID ViewBase; 874 875 /* Open the file */ 876 877 RtlInitUnicodeString(&FileName, PathNameToFile); 878 InitializeObjectAttributes(&ObjectAttributes, 879 &FileName, 880 OBJ_CASE_INSENSITIVE, 881 RootDirectory, 882 NULL); 883 884 *FileHandle = NULL; 885 *SectionHandle = NULL; 886 887 Status = NtOpenFile(FileHandle, 888 FILE_GENERIC_READ | // Contains SYNCHRONIZE 889 (ReadWriteAccess ? FILE_GENERIC_WRITE : 0), 890 &ObjectAttributes, 891 &IoStatusBlock, 892 FILE_SHARE_READ, 893 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 894 if (!NT_SUCCESS(Status)) 895 { 896 DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status); 897 return Status; 898 } 899 900 if (FileSize) 901 { 902 /* Query the file size */ 903 FILE_STANDARD_INFORMATION FileInfo; 904 Status = NtQueryInformationFile(*FileHandle, 905 &IoStatusBlock, 906 &FileInfo, 907 sizeof(FileInfo), 908 FileStandardInformation); 909 if (!NT_SUCCESS(Status)) 910 { 911 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status); 912 NtClose(*FileHandle); 913 *FileHandle = NULL; 914 return Status; 915 } 916 917 if (FileInfo.EndOfFile.HighPart != 0) 918 DPRINT1("WARNING!! The file '%wZ' is too large!\n", &FileName); 919 920 *FileSize = FileInfo.EndOfFile.LowPart; 921 922 DPRINT("File size: %lu\n", *FileSize); 923 } 924 925 /* Map the file in memory */ 926 927 SectionPageProtection = (ReadWriteAccess ? PAGE_READWRITE : PAGE_READONLY); 928 929 /* Create the section */ 930 Status = NtCreateSection(SectionHandle, 931 STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | 932 SECTION_MAP_READ | 933 (ReadWriteAccess ? SECTION_MAP_WRITE : 0), 934 NULL, 935 NULL, 936 SectionPageProtection, 937 SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */, 938 *FileHandle); 939 if (!NT_SUCCESS(Status)) 940 { 941 DPRINT1("Failed to create a memory section for file '%wZ', Status 0x%08lx\n", &FileName, Status); 942 NtClose(*FileHandle); 943 *FileHandle = NULL; 944 return Status; 945 } 946 947 /* Map the section */ 948 ViewSize = 0; 949 ViewBase = NULL; 950 Status = NtMapViewOfSection(*SectionHandle, 951 NtCurrentProcess(), 952 &ViewBase, 953 0, 0, 954 NULL, 955 &ViewSize, 956 ViewShare, 957 0, 958 SectionPageProtection); 959 if (!NT_SUCCESS(Status)) 960 { 961 DPRINT1("Failed to map a view for file '%wZ', Status 0x%08lx\n", &FileName, Status); 962 NtClose(*SectionHandle); 963 *SectionHandle = NULL; 964 NtClose(*FileHandle); 965 *FileHandle = NULL; 966 return Status; 967 } 968 969 *BaseAddress = ViewBase; 970 return STATUS_SUCCESS; 971 } 972 973 BOOLEAN 974 UnMapFile( 975 IN HANDLE SectionHandle, 976 IN PVOID BaseAddress) 977 { 978 NTSTATUS Status; 979 BOOLEAN Success = TRUE; 980 981 Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress); 982 if (!NT_SUCCESS(Status)) 983 { 984 DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n", 985 BaseAddress, Status); 986 Success = FALSE; 987 } 988 Status = NtClose(SectionHandle); 989 if (!NT_SUCCESS(Status)) 990 { 991 DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n", 992 SectionHandle, Status); 993 Success = FALSE; 994 } 995 996 return Success; 997 } 998 999 /* EOF */ 1000