3 * Copyright (C) 2002 ReactOS Team
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.
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.
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.
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)
27 /* INCLUDES *****************************************************************/
34 /* FUNCTIONS ****************************************************************/
36 static BOOLEAN HasCurrentCabinet
= FALSE
;
37 static WCHAR CurrentCabinetName
[MAX_PATH
];
38 static CAB_SEARCH Search
;
42 SetupCreateSingleDirectory(
45 OBJECT_ATTRIBUTES ObjectAttributes
;
46 IO_STATUS_BLOCK IoStatusBlock
;
47 UNICODE_STRING PathName
;
48 HANDLE DirectoryHandle
;
51 if(!RtlCreateUnicodeString(&PathName
, DirectoryName
))
52 return STATUS_NO_MEMORY
;
54 if (PathName
.Length
> sizeof(WCHAR
) &&
55 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
) - 2] == L
'\\' &&
56 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
) - 1] == L
'.')
58 PathName
.Length
-= sizeof(WCHAR
);
59 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
)] = 0;
62 if (PathName
.Length
> sizeof(WCHAR
) &&
63 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
) - 1] == L
'\\')
65 PathName
.Length
-= sizeof(WCHAR
);
66 PathName
.Buffer
[PathName
.Length
/ sizeof(WCHAR
)] = 0;
69 InitializeObjectAttributes(&ObjectAttributes
,
71 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
75 Status
= NtCreateFile(&DirectoryHandle
,
80 FILE_ATTRIBUTE_DIRECTORY
,
81 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
86 if (NT_SUCCESS(Status
))
88 NtClose(DirectoryHandle
);
91 RtlFreeUnicodeString(&PathName
);
100 PWCHAR PathBuffer
= NULL
;
102 ULONG BackslashCount
;
104 NTSTATUS Status
= STATUS_SUCCESS
;
106 Size
= (wcslen(PathName
) + 1) * sizeof(WCHAR
);
107 PathBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Size
);
108 if (PathBuffer
== NULL
)
109 return STATUS_INSUFFICIENT_RESOURCES
;
111 wcscpy(PathBuffer
, PathName
);
112 EndPtr
= PathBuffer
+ wcslen(PathName
);
116 /* Skip the '\Device\HarddiskX\PartitionY\ part */
118 while (Ptr
< EndPtr
&& BackslashCount
< 4)
132 DPRINT("PathBuffer: %S\n", PathBuffer
);
133 if (!DoesPathExist(NULL
, PathBuffer
))
135 DPRINT("Create: %S\n", PathBuffer
);
136 Status
= SetupCreateSingleDirectory(PathBuffer
);
137 if (!NT_SUCCESS(Status
))
147 if (!DoesPathExist(NULL
, PathBuffer
))
149 DPRINT("Create: %S\n", PathBuffer
);
150 Status
= SetupCreateSingleDirectory(PathBuffer
);
151 if (!NT_SUCCESS(Status
))
157 if (PathBuffer
!= NULL
)
158 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer
);
165 PWCHAR SourceFileName
,
166 PWCHAR DestinationFileName
)
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
;
175 UNICODE_STRING FileName
;
177 PVOID SourceFileMap
= 0;
178 HANDLE SourceFileSection
;
179 SIZE_T SourceSectionSize
= 0;
180 LARGE_INTEGER ByteOffset
;
182 RtlInitUnicodeString(&FileName
,
185 InitializeObjectAttributes(&ObjectAttributes
,
187 OBJ_CASE_INSENSITIVE
,
191 Status
= NtOpenFile(&FileHandleSource
,
196 FILE_SEQUENTIAL_ONLY
);
197 if (!NT_SUCCESS(Status
))
199 DPRINT1("NtOpenFile failed: %x, %wZ\n", Status
, &FileName
);
203 Status
= NtQueryInformationFile(FileHandleSource
,
206 sizeof(FILE_STANDARD_INFORMATION
),
207 FileStandardInformation
);
208 if (!NT_SUCCESS(Status
))
210 DPRINT1("NtQueryInformationFile failed: %x\n", Status
);
214 Status
= NtQueryInformationFile(FileHandleSource
,
215 &IoStatusBlock
,&FileBasic
,
216 sizeof(FILE_BASIC_INFORMATION
),
217 FileBasicInformation
);
218 if (!NT_SUCCESS(Status
))
220 DPRINT1("NtQueryInformationFile failed: %x\n", Status
);
224 Status
= NtCreateSection(&SourceFileSection
,
231 if (!NT_SUCCESS(Status
))
233 DPRINT1("NtCreateSection failed: %x, %S\n", Status
, SourceFileName
);
237 Status
= NtMapViewOfSection(SourceFileSection
,
247 if (!NT_SUCCESS(Status
))
249 DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status
, SourceFileName
);
253 RtlInitUnicodeString(&FileName
,
254 DestinationFileName
);
256 InitializeObjectAttributes(&ObjectAttributes
,
258 OBJ_CASE_INSENSITIVE
,
262 Status
= NtCreateFile(&FileHandleDest
,
263 GENERIC_WRITE
| SYNCHRONIZE
,
267 FILE_ATTRIBUTE_NORMAL
,
270 FILE_NO_INTERMEDIATE_BUFFERING
|
271 FILE_SEQUENTIAL_ONLY
|
272 FILE_SYNCHRONOUS_IO_NONALERT
,
275 if (!NT_SUCCESS(Status
))
277 /* Open may have failed because the file to overwrite
278 * is in readonly mode
280 if (Status
== STATUS_ACCESS_DENIED
)
282 FILE_BASIC_INFORMATION FileBasicInfo
;
284 /* Reattempt to open it with limited access */
285 Status
= NtCreateFile(&FileHandleDest
,
286 FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
290 FILE_ATTRIBUTE_NORMAL
,
293 FILE_NO_INTERMEDIATE_BUFFERING
|
294 FILE_SEQUENTIAL_ONLY
|
295 FILE_SYNCHRONOUS_IO_NONALERT
,
298 /* Fail for real if we cannot open it that way */
299 if (!NT_SUCCESS(Status
))
301 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status
, &FileName
);
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
312 NtSetInformationFile(FileHandleDest
, &IoStatusBlock
, &FileBasicInfo
,
313 sizeof(FileBasicInfo
), FileBasicInformation
);
316 NtClose(FileHandleDest
);
318 /* And re-attempt overwrite */
319 Status
= NtCreateFile(&FileHandleDest
,
320 GENERIC_WRITE
| SYNCHRONIZE
,
324 FILE_ATTRIBUTE_NORMAL
,
327 FILE_NO_INTERMEDIATE_BUFFERING
|
328 FILE_SEQUENTIAL_ONLY
|
329 FILE_SYNCHRONOUS_IO_NONALERT
,
335 if (!NT_SUCCESS(Status
))
337 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status
, &FileName
);
342 RegionSize
= (ULONG
)PAGE_ROUND_UP(FileStandard
.EndOfFile
.u
.LowPart
);
343 IoStatusBlock
.Status
= 0;
344 ByteOffset
.QuadPart
= 0ULL;
345 Status
= NtWriteFile(FileHandleDest
,
354 if (!NT_SUCCESS(Status
))
356 DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", Status
, IoStatusBlock
.Status
, &IoStatusBlock
, SourceFileMap
, RegionSize
);
360 /* Copy file date/time from source file */
361 Status
= NtSetInformationFile(FileHandleDest
,
364 sizeof(FILE_BASIC_INFORMATION
),
365 FileBasicInformation
);
366 if (!NT_SUCCESS(Status
))
368 DPRINT1("NtSetInformationFile failed: %x\n", Status
);
372 /* shorten the file back to it's real size after completing the write */
373 Status
= NtSetInformationFile(FileHandleDest
,
375 &FileStandard
.EndOfFile
,
376 sizeof(FILE_END_OF_FILE_INFORMATION
),
377 FileEndOfFileInformation
);
378 if (!NT_SUCCESS(Status
))
380 DPRINT1("NtSetInformationFile failed: %x\n", Status
);
384 NtClose(FileHandleDest
);
387 NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap
);
390 NtClose(SourceFileSection
);
393 NtClose(FileHandleSource
);
402 PWCHAR CabinetFileName
,
403 PWCHAR SourceFileName
,
404 PWCHAR DestinationPathName
)
408 DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n",
409 CabinetFileName
, SourceFileName
, DestinationPathName
);
411 if (HasCurrentCabinet
)
413 DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName
);
416 if ((HasCurrentCabinet
) && (wcscmp(CabinetFileName
, CurrentCabinetName
) == 0))
418 DPRINT("Using same cabinet as last time\n");
420 /* Use our last location because the files should be sequential */
421 CabStatus
= CabinetFindNextFileSequential(SourceFileName
, &Search
);
422 if (CabStatus
!= CAB_STATUS_SUCCESS
)
424 DPRINT("Sequential miss on file: %S\n", SourceFileName
);
426 /* Looks like we got unlucky */
427 CabStatus
= CabinetFindFirst(SourceFileName
, &Search
);
432 DPRINT("Using new cabinet\n");
434 if (HasCurrentCabinet
)
439 wcscpy(CurrentCabinetName
, CabinetFileName
);
442 CabinetSetEventHandlers(NULL
, NULL
, NULL
);
443 CabinetSetCabinetName(CabinetFileName
);
445 CabStatus
= CabinetOpen();
446 if (CabStatus
== CAB_STATUS_SUCCESS
)
448 DPRINT("Opened cabinet %S\n", CabinetGetCabinetName());
449 HasCurrentCabinet
= TRUE
;
453 DPRINT("Cannot open cabinet (%d)\n", CabStatus
);
454 return STATUS_UNSUCCESSFUL
;
457 /* We have to start at the beginning here */
458 CabStatus
= CabinetFindFirst(SourceFileName
, &Search
);
461 if (CabStatus
!= CAB_STATUS_SUCCESS
)
463 DPRINT1("Unable to find '%S' in cabinet '%S'\n", SourceFileName
, CabinetGetCabinetName());
464 return STATUS_UNSUCCESSFUL
;
467 CabinetSetDestinationPath(DestinationPathName
);
468 CabStatus
= CabinetExtractFile(&Search
);
469 if (CabStatus
!= CAB_STATUS_SUCCESS
)
471 DPRINT("Cannot extract file %S (%d)\n", SourceFileName
, CabStatus
);
472 return STATUS_UNSUCCESSFUL
;
475 return STATUS_SUCCESS
;
481 IN PCWSTR InstallDir
)
485 Length
= wcslen(InstallDir
);
487 // TODO: Add check for 8.3 too.
489 /* Path must be at least 2 characters long */
493 /* Path must start with a backslash */
494 // if (InstallDir[0] != L'\\')
497 /* Path must not end with a backslash */
498 if (InstallDir
[Length
- 1] == L
'\\')
501 /* Path must not contain whitespace characters */
502 for (i
= 0; i
< Length
; i
++)
504 if (iswspace(InstallDir
[i
]))
508 /* Path component must not end with a dot */
509 for (i
= 0; i
< Length
; i
++)
511 if (InstallDir
[i
] == L
'\\' && i
> 0)
513 if (InstallDir
[i
- 1] == L
'.')
518 if (InstallDir
[Length
- 1] == L
'.')
526 IN OUT PWSTR PathElem1
,
527 IN SIZE_T cchPathSize
,
528 IN PCWSTR PathElem2 OPTIONAL
)
534 return STATUS_SUCCESS
;
535 if (cchPathSize
<= 1)
536 return STATUS_SUCCESS
;
538 cchPathLen
= min(cchPathSize
, wcslen(PathElem1
));
540 if (PathElem2
[0] != L
'\\' && cchPathLen
> 0 && PathElem1
[cchPathLen
-1] != L
'\\')
542 /* PathElem2 does not start with '\' and PathElem1 does not end with '\' */
543 Status
= RtlStringCchCatW(PathElem1
, cchPathSize
, L
"\\");
544 if (!NT_SUCCESS(Status
))
547 else if (PathElem2
[0] == L
'\\' && cchPathLen
> 0 && PathElem1
[cchPathLen
-1] == L
'\\')
549 /* PathElem2 starts with '\' and PathElem1 ends with '\' */
550 while (*PathElem2
== L
'\\')
551 ++PathElem2
; // Skip any backslash
553 Status
= RtlStringCchCatW(PathElem1
, cchPathSize
, PathElem2
);
558 // NOTE: It may be possible to merge both DoesPathExist and DoesFileExist...
562 IN HANDLE RootDirectory OPTIONAL
,
567 OBJECT_ATTRIBUTES ObjectAttributes
;
568 IO_STATUS_BLOCK IoStatusBlock
;
571 RtlInitUnicodeString(&Name
, PathName
);
573 InitializeObjectAttributes(&ObjectAttributes
,
575 OBJ_CASE_INSENSITIVE
,
579 Status
= NtOpenFile(&FileHandle
,
580 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
583 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
584 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
585 if (NT_SUCCESS(Status
))
588 DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &Name
, Status
);
590 return NT_SUCCESS(Status
);
595 IN HANDLE RootDirectory OPTIONAL
,
596 IN PCWSTR PathName OPTIONAL
,
601 OBJECT_ATTRIBUTES ObjectAttributes
;
602 IO_STATUS_BLOCK IoStatusBlock
;
604 WCHAR FullName
[MAX_PATH
];
607 RtlStringCchCopyW(FullName
, ARRAYSIZE(FullName
), PathName
);
609 FullName
[0] = UNICODE_NULL
;
612 ConcatPaths(FullName
, ARRAYSIZE(FullName
), FileName
);
614 RtlInitUnicodeString(&Name
, FullName
);
616 InitializeObjectAttributes(&ObjectAttributes
,
618 OBJ_CASE_INSENSITIVE
,
622 Status
= NtOpenFile(&FileHandle
,
623 GENERIC_READ
| SYNCHRONIZE
,
626 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
627 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
628 if (NT_SUCCESS(Status
))
631 DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &Name
, Status
);
633 return NT_SUCCESS(Status
);
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 '\\').
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.
647 * NOTE: The function does not accept leading whitespace.
650 NtPathToDiskPartComponents(
652 OUT PULONG pDiskNumber
,
653 OUT PULONG pPartNumber
,
654 OUT PCWSTR
* PathComponent OPTIONAL
)
656 ULONG DiskNumber
, PartNumber
;
661 if (PathComponent
) *PathComponent
= NULL
;
665 if (_wcsnicmp(Path
, L
"\\Device\\Harddisk", 16) != 0)
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
);
674 /* A number must be present now */
675 if (!iswdigit(*Path
))
677 DPRINT1("'%S' : expected a number! Not a regular hard disk device.\n", Path
);
680 DiskNumber
= wcstoul(Path
, (PWSTR
*)&Path
, 10);
682 /* Either NULL termination, or a path separator must be present now */
685 DPRINT1("An error happened!\n");
688 else if (*Path
&& *Path
!= OBJ_NAME_PATH_SEPARATOR
)
690 DPRINT1("'%S' : expected a path separator!\n", Path
);
696 DPRINT1("The path only specified a hard disk (and nothing else, like a partition...), so we stop there.\n");
700 /* Here, *Path == L'\\' */
702 if (_wcsnicmp(Path
, L
"\\Partition", 10) != 0)
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
);
711 /* A number must be present now */
712 if (!iswdigit(*Path
))
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
);
718 PartNumber
= wcstoul(Path
, (PWSTR
*)&Path
, 10);
720 /* Either NULL termination, or a path separator must be present now */
723 /* We fail here because wcstoul failed for whatever reason */
724 DPRINT1("An error happened!\n");
727 else if (*Path
&& *Path
!= OBJ_NAME_PATH_SEPARATOR
)
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
);
734 /* OK, here we really have a partition specifier: return its number */
735 *pPartNumber
= PartNumber
;
738 /* Return the disk number */
739 *pDiskNumber
= DiskNumber
;
741 /* Return the path component also, if the user wants it */
742 if (PathComponent
) *PathComponent
= Path
;
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
)
758 OBJECT_ATTRIBUTES ObjectAttributes
;
759 IO_STATUS_BLOCK IoStatusBlock
;
763 WCHAR FullName
[MAX_PATH
];
766 RtlStringCchCopyW(FullName
, ARRAYSIZE(FullName
), PathName
);
768 FullName
[0] = UNICODE_NULL
;
771 ConcatPaths(FullName
, ARRAYSIZE(FullName
), FileName
);
773 RtlInitUnicodeString(&Name
, FullName
);
775 InitializeObjectAttributes(&ObjectAttributes
,
777 OBJ_CASE_INSENSITIVE
,
782 *SectionHandle
= NULL
;
784 Status
= NtOpenFile(FileHandle
,
785 GENERIC_READ
| SYNCHRONIZE
,
789 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
790 if (!NT_SUCCESS(Status
))
792 DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &Name
, Status
);
798 /* Query the file size */
799 FILE_STANDARD_INFORMATION FileInfo
;
800 Status
= NtQueryInformationFile(*FileHandle
,
804 FileStandardInformation
);
805 if (!NT_SUCCESS(Status
))
807 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status
);
808 NtClose(*FileHandle
);
813 if (FileInfo
.EndOfFile
.HighPart
!= 0)
814 DPRINT1("WARNING!! The file %wZ is too large!\n", Name
);
816 *FileSize
= FileInfo
.EndOfFile
.LowPart
;
818 DPRINT("File size: %lu\n", *FileSize
);
821 /* Map the file in memory */
823 /* Create the section */
824 Status
= NtCreateSection(SectionHandle
,
829 SEC_COMMIT
/* | SEC_IMAGE (_NO_EXECUTE) */,
831 if (!NT_SUCCESS(Status
))
833 DPRINT1("Failed to create a memory section for file %wZ, Status 0x%08lx\n", &Name
, Status
);
834 NtClose(*FileHandle
);
839 /* Map the section */
842 Status
= NtMapViewOfSection(*SectionHandle
,
851 if (!NT_SUCCESS(Status
))
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
);
861 *BaseAddress
= ViewBase
;
862 return STATUS_SUCCESS
;
867 IN HANDLE SectionHandle
,
868 IN PVOID BaseAddress
)
871 BOOLEAN Success
= TRUE
;
873 Status
= NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress
);
874 if (!NT_SUCCESS(Status
))
876 DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n",
877 BaseAddress
, Status
);
880 Status
= NtClose(SectionHandle
);
881 if (!NT_SUCCESS(Status
))
883 DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n",
884 SectionHandle
, Status
);