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