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