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