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