[USETUP] Introduce an NT OS installation detector: a functionality that attempts...
[reactos.git] / base / setup / usetup / osdetect.c
1 /*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: NT 5.x family (MS Windows <= 2003, and ReactOS)
5 * operating systems detection code.
6 * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
7 */
8
9 #include "usetup.h"
10
11 #include <ndk/ldrtypes.h>
12 #include <ndk/ldrfuncs.h>
13
14 // HACK!
15 #include <strsafe.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20
21 /* GLOBALS ******************************************************************/
22
23 extern PPARTLIST PartitionList;
24
25 /* Language-independent Vendor strings */
26 static const PCWSTR KnownVendors[] = { L"ReactOS", L"Microsoft" };
27
28
29 /* VERSION RESOURCE API ******************************************************/
30
31 /*
32 * NT-oriented version resource management, adapted from dll/win32/version.
33 * We only deal with 32-bit PE executables.
34 */
35
36 NTSTATUS
37 NtGetVersionResource(
38 IN PVOID BaseAddress,
39 OUT PVOID* Resource,
40 OUT PULONG ResourceSize OPTIONAL)
41 {
42 // #define RT_VERSION MAKEINTRESOURCE(16) // See winuser.h
43 #define VS_VERSION_INFO 1 // See psdk/verrsrc.h
44 #define VS_FILE_INFO RT_VERSION
45
46 NTSTATUS Status;
47 LDR_RESOURCE_INFO ResourceInfo;
48 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
49 PVOID Data = NULL;
50 ULONG Size = 0;
51
52 /* Try to find the resource */
53 ResourceInfo.Type = 16; // RT_VERSION;
54 ResourceInfo.Name = VS_VERSION_INFO; // MAKEINTRESOURCEW(VS_VERSION_INFO);
55 ResourceInfo.Language = 0; // Don't care about the language
56
57 Status = LdrFindResource_U(BaseAddress,
58 &ResourceInfo,
59 RESOURCE_DATA_LEVEL,
60 &ResourceDataEntry);
61 if (!NT_SUCCESS(Status))
62 {
63 DPRINT1("NtGetVersionResource: Version resource not found, Status 0x%08lx\n", Status);
64 return Status;
65 }
66
67 /* Access the resource */
68 Status = LdrAccessResource(BaseAddress,
69 ResourceDataEntry,
70 &Data,
71 &Size);
72 if (!NT_SUCCESS(Status))
73 {
74 DPRINT1("NtGetVersionResource: Cannot access Version resource, Status 0x%08lx\n", Status);
75 return Status;
76 }
77
78 *Resource = Data;
79 if (ResourceSize) *ResourceSize = Size;
80
81 return STATUS_SUCCESS;
82 }
83
84 /* NOTE: the xxx_STRUCT16 version differs by storing strings in ANSI, not in UNICODE */
85 typedef struct _VS_VERSION_INFO_STRUCT32
86 {
87 WORD wLength;
88 WORD wValueLength;
89 WORD wType; /* 1:Text, 0:Binary */
90 WCHAR szKey[1];
91 #if 0 /* variable length structure */
92 /* DWORD aligned */
93 BYTE Value[];
94 /* DWORD aligned */
95 VS_VERSION_INFO_STRUCT32 Children[];
96 #endif
97 } VS_VERSION_INFO_STRUCT32, *PVS_VERSION_INFO_STRUCT32;
98 typedef const VS_VERSION_INFO_STRUCT32 *PCVS_VERSION_INFO_STRUCT32;
99
100 #define DWORD_ALIGN( base, ptr ) \
101 ( (ULONG_PTR)(base) + ((((ULONG_PTR)(ptr) - (ULONG_PTR)(base)) + 3) & ~3) )
102
103 #define VersionInfo32_Value( ver ) \
104 DWORD_ALIGN( (ver), (ver)->szKey + wcslen((ver)->szKey) + 1 )
105
106 #define VersionInfo32_Children( ver ) \
107 (PCVS_VERSION_INFO_STRUCT32)( VersionInfo32_Value( ver ) + \
108 ( ( (ver)->wValueLength * \
109 ((ver)->wType? 2 : 1) + 3 ) & ~3 ) )
110
111 #define VersionInfo32_Next( ver ) \
112 (PVS_VERSION_INFO_STRUCT32)( (ULONG_PTR)ver + (((ver)->wLength + 3) & ~3) )
113
114 static PCVS_VERSION_INFO_STRUCT32
115 VersionInfo32_FindChild(
116 IN PCVS_VERSION_INFO_STRUCT32 info,
117 IN PCWSTR szKey,
118 IN UINT cbKey)
119 {
120 PCVS_VERSION_INFO_STRUCT32 child = VersionInfo32_Children(info);
121
122 while ((ULONG_PTR)child < (ULONG_PTR)info + info->wLength)
123 {
124 if (!_wcsnicmp(child->szKey, szKey, cbKey) && !child->szKey[cbKey])
125 return child;
126
127 if (child->wLength == 0) return NULL;
128 child = VersionInfo32_Next(child);
129 }
130
131 return NULL;
132 }
133
134 static NTSTATUS
135 VersionInfo32_QueryValue(
136 IN PCVS_VERSION_INFO_STRUCT32 info,
137 IN PCWSTR lpSubBlock,
138 OUT PVOID* lplpBuffer,
139 OUT PUINT puLen OPTIONAL,
140 OUT BOOL* pbText OPTIONAL)
141 {
142 PCWSTR lpNextSlash;
143
144 DPRINT("lpSubBlock : (%S)\n", lpSubBlock);
145
146 while (*lpSubBlock)
147 {
148 /* Find next path component */
149 for (lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++)
150 {
151 if (*lpNextSlash == '\\')
152 break;
153 }
154
155 /* Skip empty components */
156 if (lpNextSlash == lpSubBlock)
157 {
158 lpSubBlock++;
159 continue;
160 }
161
162 /* We have a non-empty component: search info for key */
163 info = VersionInfo32_FindChild(info, lpSubBlock, lpNextSlash - lpSubBlock);
164 if (!info)
165 {
166 if (puLen) *puLen = 0;
167 return STATUS_RESOURCE_TYPE_NOT_FOUND;
168 }
169
170 /* Skip path component */
171 lpSubBlock = lpNextSlash;
172 }
173
174 /* Return value */
175 *lplpBuffer = (PVOID)VersionInfo32_Value(info);
176 if (puLen)
177 *puLen = info->wValueLength;
178 if (pbText)
179 *pbText = info->wType;
180
181 return STATUS_SUCCESS;
182 }
183
184 NTSTATUS
185 NtVerQueryValue(
186 IN const VOID* pBlock,
187 IN PCWSTR lpSubBlock,
188 OUT PVOID* lplpBuffer,
189 OUT PUINT puLen)
190 {
191 PCVS_VERSION_INFO_STRUCT32 info = pBlock;
192
193 DPRINT("%s (%p, %S, %p, %p)\n", __FUNCTION__, pBlock, lpSubBlock, lplpBuffer, puLen);
194
195 if (!pBlock)
196 return FALSE;
197
198 if (!lpSubBlock || !*lpSubBlock)
199 lpSubBlock = L"\\";
200
201 return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL);
202 }
203
204
205
206 /* FUNCTIONS ****************************************************************/
207
208 #if 0
209
210 BOOL IsWindowsOS(VOID)
211 {
212 // TODO:
213 // Load the "SystemRoot\System32\Config\SOFTWARE" hive and mount it,
214 // then go to (SOFTWARE\\)Microsoft\\Windows NT\\CurrentVersion,
215 // check the REG_SZ value "ProductName" and see whether it's "Windows"
216 // or "ReactOS". One may also check the REG_SZ "CurrentVersion" value,
217 // the REG_SZ "SystemRoot" and "PathName" values (what are the differences??).
218 //
219 // Optionally, looking at the SYSTEM hive, CurrentControlSet\\Control,
220 // REG_SZ values "SystemBootDevice" (and "FirmwareBootDevice" ??)...
221 //
222
223 /* ReactOS reports as Windows NT 5.2 */
224 HKEY hKey = NULL;
225
226 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
227 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
228 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
229 {
230 LONG ret;
231 DWORD dwType = 0, dwBufSize = 0;
232
233 ret = RegQueryValueExW(hKey, L"ProductName", NULL, &dwType, NULL, &dwBufSize);
234 if (ret == ERROR_SUCCESS && dwType == REG_SZ)
235 {
236 LPTSTR lpszProductName = (LPTSTR)MemAlloc(0, dwBufSize);
237 RegQueryValueExW(hKey, L"ProductName", NULL, &dwType, (LPBYTE)lpszProductName, &dwBufSize);
238
239 bIsWindowsOS = (FindSubStrI(lpszProductName, _T("Windows")) != NULL);
240
241 MemFree(lpszProductName);
242 }
243
244 RegCloseKey(hKey);
245 }
246
247 return bIsWindowsOS;
248 }
249
250 #endif
251
252 typedef enum _NTOS_BOOT_LOADER_TYPE
253 {
254 FreeLdr, // ReactOS' FreeLDR
255 NtLdr, // Windows <= 2k3 NTLDR
256 // BootMgr, // Vista+ BCD-oriented BOOTMGR
257 } NTOS_BOOT_LOADER_TYPE;
258
259 typedef struct _NTOS_BOOT_LOADER_FILES
260 {
261 NTOS_BOOT_LOADER_TYPE Type;
262 PCWSTR LoaderExecutable;
263 PCWSTR LoaderConfigurationFile;
264 // EnumerateInstallations;
265 } NTOS_BOOT_LOADER_FILES, *PNTOS_BOOT_LOADER_FILES;
266
267 // Question 1: What if config file is optional?
268 // Question 2: What if many config files are possible?
269 NTOS_BOOT_LOADER_FILES NtosBootLoaders[] =
270 {
271 {FreeLdr, L"freeldr.sys", L"freeldr.ini"},
272 {NtLdr , L"ntldr" , L"boot.ini"},
273 {NtLdr , L"setupldr" , L"txtsetup.sif"},
274 // {BootMgr, L"bootmgr" , ???}
275 };
276
277 #if 0
278
279 static VOID
280 EnumerateInstallationsFreeLdr()
281 {
282 NTSTATUS Status;
283 PINICACHE IniCache;
284 PINICACHESECTION IniSection;
285 PINICACHESECTION OsIniSection;
286 WCHAR SectionName[80];
287 WCHAR OsName[80];
288 WCHAR SystemPath[200];
289 WCHAR SectionName2[200];
290 PWCHAR KeyData;
291 ULONG i,j;
292
293 /* Open an *existing* FreeLdr.ini configuration file */
294 Status = IniCacheLoad(&IniCache, IniPath, FALSE);
295 if (!NT_SUCCESS(Status))
296 return Status;
297
298 /* Get "Operating Systems" section */
299 IniSection = IniCacheGetSection(IniCache, L"Operating Systems");
300 if (IniSection == NULL)
301 {
302 IniCacheDestroy(IniCache);
303 return STATUS_UNSUCCESSFUL;
304 }
305
306 /* NOTE that we enumerate all the valid installations, not just the default one */
307 while (TRUE)
308 {
309 Status = IniCacheGetKey(IniSection, SectionName, &KeyData);
310 if (!NT_SUCCESS(Status))
311 break;
312
313 // TODO some foobaring...
314
315 // TODO 2 : Remind the entry name so that we may display it as available installation...
316
317 /* Search for an existing ReactOS entry */
318 OsIniSection = IniCacheGetSection(IniCache, SectionName2);
319 if (OsIniSection != NULL)
320 {
321 BOOLEAN UseExistingEntry = TRUE;
322
323 /* Check for supported boot type "Windows2003" */
324 Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData);
325 if (NT_SUCCESS(Status))
326 {
327 if ((KeyData == NULL) ||
328 ( (_wcsicmp(KeyData, L"Windows2003") != 0) &&
329 (_wcsicmp(KeyData, L"\"Windows2003\"") != 0) ))
330 {
331 /* This is not a ReactOS entry */
332 UseExistingEntry = FALSE;
333 }
334 }
335 else
336 {
337 UseExistingEntry = FALSE;
338 }
339
340 if (UseExistingEntry)
341 {
342 /* BootType is Windows2003. Now check SystemPath. */
343 Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData);
344 if (NT_SUCCESS(Status))
345 {
346 swprintf(SystemPath, L"\"%s\"", ArcPath);
347 if ((KeyData == NULL) ||
348 ( (_wcsicmp(KeyData, ArcPath) != 0) &&
349 (_wcsicmp(KeyData, SystemPath) != 0) ))
350 {
351 /* This entry is a ReactOS entry, but the SystemRoot
352 does not match the one we are looking for. */
353 UseExistingEntry = FALSE;
354 }
355 }
356 else
357 {
358 UseExistingEntry = FALSE;
359 }
360 }
361 }
362 }
363
364 IniCacheDestroy(IniCache);
365 }
366
367 #endif
368
369
370 /*
371 * FindSubStrI(PCWSTR str, PCWSTR strSearch) :
372 * Searches for a sub-string 'strSearch' inside 'str', similarly to what
373 * wcsstr(str, strSearch) does, but ignores the case during the comparisons.
374 */
375 PWSTR FindSubStrI(PCWSTR str, PCWSTR strSearch)
376 {
377 PWSTR cp = (PWSTR)str;
378 PWSTR s1, s2;
379
380 if (!*strSearch)
381 return (PWSTR)str;
382
383 while (*cp)
384 {
385 s1 = cp;
386 s2 = (PWSTR)strSearch;
387
388 while (*s1 && *s2 && (towupper(*s1) == towupper(*s2)))
389 ++s1, ++s2;
390
391 if (!*s2)
392 return cp;
393
394 ++cp;
395 }
396
397 return NULL;
398 }
399
400 static BOOLEAN
401 CheckForValidPEAndVendor(
402 IN HANDLE RootDirectory OPTIONAL,
403 IN PCWSTR PathName OPTIONAL,
404 IN PCWSTR FileName, // OPTIONAL
405 IN PCWSTR VendorName // Better would be OUT PCWSTR*, and the function returning NTSTATUS ?
406 )
407 {
408 BOOLEAN Success = FALSE;
409 NTSTATUS Status;
410 HANDLE FileHandle, SectionHandle;
411 // SIZE_T ViewSize;
412 PVOID ViewBase;
413 PVOID VersionBuffer = NULL; // Read-only
414 PVOID pvData = NULL;
415 UINT BufLen = 0;
416
417 Status = OpenAndMapFile(RootDirectory, PathName, FileName,
418 &FileHandle, &SectionHandle, &ViewBase);
419 if (!NT_SUCCESS(Status))
420 {
421 DPRINT1("Failed to open and map file %wZ, Status 0x%08lx\n", &FileName, Status);
422 return FALSE; // Status;
423 }
424
425 /* Make sure it's a valid PE file */
426 if (!RtlImageNtHeader(ViewBase))
427 {
428 DPRINT1("File %wZ does not seem to be a valid PE, bail out\n", &FileName);
429 Status = STATUS_INVALID_IMAGE_FORMAT;
430 goto UnmapFile;
431 }
432
433 /*
434 * Search for a valid executable version and vendor.
435 * NOTE: The module is loaded as a data file, it should be marked as such.
436 */
437 Status = NtGetVersionResource((PVOID)((ULONG_PTR)ViewBase | 1), &VersionBuffer, NULL);
438 if (!NT_SUCCESS(Status))
439 {
440 DPRINT1("Failed to get version resource for file %wZ, Status 0x%08lx\n", &FileName, Status);
441 goto UnmapFile;
442 }
443
444 Status = NtVerQueryValue(VersionBuffer, L"\\VarFileInfo\\Translation", &pvData, &BufLen);
445 if (NT_SUCCESS(Status))
446 {
447 USHORT wCodePage = 0, wLangID = 0;
448 WCHAR FileInfo[MAX_PATH];
449 UNICODE_STRING Vendor;
450
451 wCodePage = LOWORD(*(ULONG*)pvData);
452 wLangID = HIWORD(*(ULONG*)pvData);
453
454 StringCchPrintfW(FileInfo, ARRAYSIZE(FileInfo),
455 L"StringFileInfo\\%04X%04X\\CompanyName",
456 wCodePage, wLangID);
457
458 Status = NtVerQueryValue(VersionBuffer, FileInfo, &pvData, &BufLen);
459 if (NT_SUCCESS(Status) && pvData)
460 {
461 /* BufLen includes the NULL terminator count */
462 RtlInitEmptyUnicodeString(&Vendor, pvData, BufLen * sizeof(WCHAR));
463 Vendor.Length = Vendor.MaximumLength - sizeof(UNICODE_NULL);
464
465 DPRINT1("Found version vendor: \"%wZ\" for file %wZ\n", &Vendor, &FileName);
466
467 Success = !!FindSubStrI(pvData, VendorName);
468 }
469 else
470 {
471 DPRINT1("No version vendor found for file %wZ\n", &FileName);
472 }
473 }
474
475 UnmapFile:
476 /* Finally, unmap and close the file */
477 UnMapFile(SectionHandle, ViewBase);
478 NtClose(FileHandle);
479
480 return Success;
481 }
482
483 static BOOLEAN
484 IsValidNTOSInstallation(
485 IN HANDLE PartitionHandle,
486 IN PCWSTR SystemRoot)
487 {
488 BOOLEAN Success = FALSE;
489 USHORT i;
490 WCHAR PathBuffer[MAX_PATH];
491
492 // DoesPathExist(PartitionHandle, SystemRoot, L"System32\\"); etc...
493
494 /* Check for the existence of \SystemRoot\System32 */
495 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot, L"System32\\");
496 if (!DoesPathExist(PartitionHandle, PathBuffer))
497 {
498 // DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &FileName, Status);
499 return FALSE;
500 }
501
502 /* Check for the existence of \SystemRoot\System32\drivers */
503 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot, L"System32\\drivers\\");
504 if (!DoesPathExist(PartitionHandle, PathBuffer))
505 {
506 // DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &FileName, Status);
507 return FALSE;
508 }
509
510 /* Check for the existence of \SystemRoot\System32\config */
511 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot, L"System32\\config\\");
512 if (!DoesPathExist(PartitionHandle, PathBuffer))
513 {
514 // DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &FileName, Status);
515 return FALSE;
516 }
517
518 #if 0
519 /*
520 * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config
521 * (but we don't check here whether they are actually valid).
522 */
523 if (!DoesFileExist(PartitionHandle, SystemRoot, L"System32\\config\\SYSTEM"))
524 {
525 // DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &FileName, Status);
526 return FALSE;
527 }
528 if (!DoesFileExist(PartitionHandle, SystemRoot, L"System32\\config\\SOFTWARE"))
529 {
530 // DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &FileName, Status);
531 return FALSE;
532 }
533 #endif
534
535 for (i = 0; i < ARRAYSIZE(KnownVendors); ++i)
536 {
537 /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and verify its version */
538 Success = CheckForValidPEAndVendor(PartitionHandle, SystemRoot, L"System32\\ntoskrnl.exe", KnownVendors[i]);
539
540 /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
541
542 /* Check for the existence of \SystemRoot\System32\ntdll.dll */
543 Success = CheckForValidPEAndVendor(PartitionHandle, SystemRoot, L"System32\\ntdll.dll", KnownVendors[i]);
544
545 /* We have found a correct vendor combination */
546 if (Success)
547 break;
548 }
549
550 return Success;
551 }
552
553 static VOID
554 ListNTOSInstalls(
555 IN PGENERIC_LIST List)
556 {
557 PGENERIC_LIST_ENTRY Entry;
558 PNTOS_INSTALLATION NtOsInstall;
559 ULONG NtOsInstallsCount = GetNumberOfListEntries(List);
560
561 DPRINT1("There %s %d installation%s detected:\n",
562 NtOsInstallsCount >= 2 ? "are" : "is",
563 NtOsInstallsCount,
564 NtOsInstallsCount >= 2 ? "s" : "");
565
566 Entry = GetFirstListEntry(List);
567 while (Entry)
568 {
569 NtOsInstall = (PNTOS_INSTALLATION)GetListEntryUserData(Entry);
570 Entry = GetNextListEntry(Entry);
571
572 DPRINT1(" On disk #%d, partition #%d: Installation \"%S\" in SystemRoot %S\n",
573 NtOsInstall->DiskNumber, NtOsInstall->PartitionNumber,
574 NtOsInstall->InstallationName, NtOsInstall->SystemRoot);
575 }
576
577 DPRINT1("Done.\n");
578 }
579
580 static PNTOS_INSTALLATION
581 FindExistingNTOSInstall(
582 IN PGENERIC_LIST List,
583 IN ULONG DiskNumber,
584 IN ULONG PartitionNumber,
585 IN PCWSTR SystemRoot)
586 {
587 PGENERIC_LIST_ENTRY Entry;
588 PNTOS_INSTALLATION NtOsInstall;
589
590 Entry = GetFirstListEntry(List);
591 while (Entry)
592 {
593 NtOsInstall = (PNTOS_INSTALLATION)GetListEntryUserData(Entry);
594 Entry = GetNextListEntry(Entry);
595
596 if (NtOsInstall->DiskNumber == DiskNumber &&
597 NtOsInstall->PartitionNumber == PartitionNumber &&
598 _wcsicmp(NtOsInstall->SystemRoot, SystemRoot) == 0)
599 {
600 /* Found it! */
601 return NtOsInstall;
602 }
603 }
604
605 return NULL;
606 }
607
608 static PNTOS_INSTALLATION
609 AddNTOSInstallation(
610 IN PGENERIC_LIST List,
611 IN ULONG DiskNumber,
612 IN ULONG PartitionNumber,
613 IN PCWSTR SystemRoot,
614 IN PCWSTR InstallationName)
615 {
616 PNTOS_INSTALLATION NtOsInstall;
617 CHAR InstallNameA[MAX_PATH];
618
619 /* Is there already any installation with these settings? */
620 NtOsInstall = FindExistingNTOSInstall(List, DiskNumber, PartitionNumber, SystemRoot);
621 if (NtOsInstall)
622 {
623 DPRINT1("An NTOS installation with name \"%S\" already exists on disk #%d, partition #%d, in SystemRoot %S\n",
624 NtOsInstall->InstallationName, NtOsInstall->DiskNumber, NtOsInstall->PartitionNumber, NtOsInstall->SystemRoot);
625 return NtOsInstall;
626 }
627
628 /* None was found, so add a new one */
629 NtOsInstall = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(*NtOsInstall));
630 if (!NtOsInstall)
631 return NULL;
632
633 NtOsInstall->DiskNumber = DiskNumber;
634 NtOsInstall->PartitionNumber = PartitionNumber;
635 StringCchCopyW(NtOsInstall->SystemRoot, ARRAYSIZE(NtOsInstall->SystemRoot), SystemRoot);
636 StringCchCopyW(NtOsInstall->InstallationName, ARRAYSIZE(NtOsInstall->InstallationName), InstallationName);
637
638 // Having the GENERIC_LIST storing the display item string plainly sucks...
639 StringCchPrintfA(InstallNameA, ARRAYSIZE(InstallNameA), "%S", InstallationName);
640 AppendGenericListEntry(List, InstallNameA, NtOsInstall, FALSE);
641
642 return NtOsInstall;
643 }
644
645 static VOID
646 FindNTOSInstallations(
647 IN PGENERIC_LIST List,
648 IN ULONG DiskNumber,
649 IN ULONG PartitionNumber)
650 {
651 NTSTATUS Status;
652 UINT i;
653 HANDLE PartitionHandle, FileHandle;
654 OBJECT_ATTRIBUTES ObjectAttributes;
655 IO_STATUS_BLOCK IoStatusBlock;
656 UNICODE_STRING PartitionRootPath;
657 HANDLE SectionHandle;
658 // SIZE_T ViewSize;
659 PVOID ViewBase;
660 WCHAR PathBuffer[MAX_PATH];
661 WCHAR SystemRoot[MAX_PATH];
662 WCHAR InstallNameW[MAX_PATH];
663
664 /* Set PartitionRootPath */
665 swprintf(PathBuffer,
666 L"\\Device\\Harddisk%lu\\Partition%lu\\",
667 DiskNumber, PartitionNumber);
668 RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
669 DPRINT1("FindNTOSInstallations: PartitionRootPath: %wZ\n", &PartitionRootPath);
670
671 /* Open the partition */
672 InitializeObjectAttributes(&ObjectAttributes,
673 &PartitionRootPath,
674 OBJ_CASE_INSENSITIVE,
675 NULL,
676 NULL);
677 Status = NtOpenFile(&PartitionHandle,
678 FILE_LIST_DIRECTORY | SYNCHRONIZE,
679 &ObjectAttributes,
680 &IoStatusBlock,
681 FILE_SHARE_READ | FILE_SHARE_WRITE,
682 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
683 if (!NT_SUCCESS(Status))
684 {
685 DPRINT1("Failed to open partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status);
686 return;
687 }
688
689 /* Try to see whether we recognize some NT boot loaders */
690 for (i = 0; i < ARRAYSIZE(NtosBootLoaders); ++i)
691 {
692 /* Check whether the loader executable exists */
693 if (!DoesFileExist(PartitionHandle, NULL, NtosBootLoaders[i].LoaderExecutable))
694 {
695 /* The loader does not exist, continue with another one */
696 DPRINT1("Loader executable %S does not exist, continue with another one...\n", NtosBootLoaders[i].LoaderExecutable);
697 continue;
698 }
699
700 /* Check whether the loader configuration file exists */
701 Status = OpenAndMapFile(PartitionHandle, NULL, NtosBootLoaders[i].LoaderConfigurationFile,
702 &FileHandle, &SectionHandle, &ViewBase);
703 if (!NT_SUCCESS(Status))
704 {
705 /* The loader does not exist, continue with another one */
706 // FIXME: Consider it might be optional??
707 DPRINT1("Loader configuration file %S does not exist, continue with another one...\n", NtosBootLoaders[i].LoaderConfigurationFile);
708 continue;
709 }
710
711 /* The loader configuration file exists, interpret it to find valid installations */
712 // TODO!!
713 DPRINT1("TODO: Analyse the OS installations inside %S !\n", NtosBootLoaders[i].LoaderConfigurationFile);
714
715 // Here we get a SystemRootPath for each installation // FIXME!
716 // FIXME: Do NOT hardcode the path!! But retrieve it from boot.ini etc...
717 StringCchCopyW(SystemRoot, ARRAYSIZE(SystemRoot), L"WINDOWS\\");
718 if (IsValidNTOSInstallation(PartitionHandle, SystemRoot))
719 {
720 DPRINT1("Found a valid NTOS installation in disk #%d, partition #%d, SystemRoot %S\n",
721 DiskNumber, PartitionNumber, SystemRoot);
722 StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C: \\Device\\Harddisk%lu\\Partition%lu\\%s \"%s\"",
723 'X' /* FIXME: Partition letter */, DiskNumber, PartitionNumber, SystemRoot, L"Windows (placeholder)");
724 AddNTOSInstallation(List, DiskNumber, PartitionNumber, SystemRoot, InstallNameW);
725 }
726
727 // Here we get a SystemRootPath for each installation // FIXME!
728 // FIXME: Do NOT hardcode the path!! But retrieve it from boot.ini etc...
729 StringCchCopyW(SystemRoot, ARRAYSIZE(SystemRoot), L"ReactOS\\");
730 if (IsValidNTOSInstallation(PartitionHandle, SystemRoot))
731 {
732 DPRINT1("Found a valid NTOS installation in disk #%d, partition #%d, SystemRoot %S\n",
733 DiskNumber, PartitionNumber, SystemRoot);
734 StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C: \\Device\\Harddisk%lu\\Partition%lu\\%s \"%s\"",
735 'X' /* FIXME: Partition letter */, DiskNumber, PartitionNumber, SystemRoot, L"ReactOS (placeholder)");
736 AddNTOSInstallation(List, DiskNumber, PartitionNumber, SystemRoot, InstallNameW);
737 }
738
739 /* Finally, unmap and close the file */
740 UnMapFile(SectionHandle, ViewBase);
741 NtClose(FileHandle);
742 }
743
744 /* Close the partition */
745 NtClose(PartitionHandle);
746 }
747
748 // static
749 FORCEINLINE BOOLEAN
750 ShouldICheckThisPartition(
751 IN PPARTENTRY PartEntry)
752 {
753 if (!PartEntry)
754 return FALSE;
755
756 return PartEntry->IsPartitioned &&
757 !IsContainerPartition(PartEntry->PartitionType) /* alternatively: PartEntry->PartitionNumber != 0 */ &&
758 !PartEntry->New &&
759 (PartEntry->FormatState == Preformatted /* || PartEntry->FormatState == Formatted */);
760 }
761
762 // EnumerateNTOSInstallations
763 PGENERIC_LIST
764 CreateNTOSInstallationsList(
765 IN PPARTLIST PartList)
766 {
767 PGENERIC_LIST List;
768 PLIST_ENTRY Entry, Entry2;
769 PDISKENTRY DiskEntry;
770 PPARTENTRY PartEntry;
771
772 List = CreateGenericList();
773 if (List == NULL)
774 return NULL;
775
776 /* Loop each available disk ... */
777 Entry = PartList->DiskListHead.Flink;
778 while (Entry != &PartList->DiskListHead)
779 {
780 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
781 Entry = Entry->Flink;
782
783 DPRINT1("Disk #%d\n", DiskEntry->DiskNumber);
784
785 /* ... and for each disk, loop each available partition */
786
787 /* First, the primary partitions */
788 Entry2 = DiskEntry->PrimaryPartListHead.Flink;
789 while (Entry2 != &DiskEntry->PrimaryPartListHead)
790 {
791 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
792 Entry2 = Entry2->Flink;
793
794 DPRINT1(" Primary Partition #%d, index %d - Type 0x%02x, IsLogical = %s, IsPartitioned = %s, IsNew = %s, AutoCreate = %s, FormatState = %lu -- Should I check it? %s\n",
795 PartEntry->PartitionNumber, PartEntry->PartitionIndex,
796 PartEntry->PartitionType, PartEntry->LogicalPartition ? "TRUE" : "FALSE",
797 PartEntry->IsPartitioned ? "TRUE" : "FALSE",
798 PartEntry->New ? "Yes" : "No",
799 PartEntry->AutoCreate ? "Yes" : "No",
800 PartEntry->FormatState,
801 ShouldICheckThisPartition(PartEntry) ? "YES!" : "NO!");
802
803 if (ShouldICheckThisPartition(PartEntry))
804 FindNTOSInstallations(List, DiskEntry->DiskNumber, PartEntry->PartitionNumber);
805 }
806
807 /* Then, the logical partitions (present in the extended partition) */
808 Entry2 = DiskEntry->LogicalPartListHead.Flink;
809 while (Entry2 != &DiskEntry->LogicalPartListHead)
810 {
811 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
812 Entry2 = Entry2->Flink;
813
814 DPRINT1(" Logical Partition #%d, index %d - Type 0x%02x, IsLogical = %s, IsPartitioned = %s, IsNew = %s, AutoCreate = %s, FormatState = %lu -- Should I check it? %s\n",
815 PartEntry->PartitionNumber, PartEntry->PartitionIndex,
816 PartEntry->PartitionType, PartEntry->LogicalPartition ? "TRUE" : "FALSE",
817 PartEntry->IsPartitioned ? "TRUE" : "FALSE",
818 PartEntry->New ? "Yes" : "No",
819 PartEntry->AutoCreate ? "Yes" : "No",
820 PartEntry->FormatState,
821 ShouldICheckThisPartition(PartEntry) ? "YES!" : "NO!");
822
823 if (ShouldICheckThisPartition(PartEntry))
824 FindNTOSInstallations(List, DiskEntry->DiskNumber, PartEntry->PartitionNumber);
825 }
826 }
827
828 /**** Debugging: List all the collected installations ****/
829 ListNTOSInstalls(List);
830
831 return List;
832 }
833
834 /* EOF */