xref: /reactos/base/setup/lib/bootsup.c (revision b3cd5767)
1 /*
2  * PROJECT:     ReactOS Setup Library
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Bootloader support functions
5  * COPYRIGHT:   ...
6  *              Copyright 2017-2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include "precomp.h"
12 
13 #include <ntddstor.h> // For STORAGE_DEVICE_NUMBER
14 
15 #include "bldrsup.h"
16 #include "devutils.h"
17 #include "filesup.h"
18 #include "partlist.h"
19 #include "bootcode.h"
20 #include "fsutil.h"
21 
22 #include "setuplib.h" // HAXX for IsUnattendedSetup!!
23 
24 #include "bootsup.h"
25 
26 #define NDEBUG
27 #include <debug.h>
28 
29 /*
30  * BIG FIXME!!
31  * ===========
32  *
33  * bootsup.c can deal with MBR code (actually it'll have at some point
34  * to share or give it to partlist.c, because when we'll support GPT disks,
35  * things will change a bit).
36  * And, bootsup.c can manage initializing / adding boot entries into NTLDR
37  * and FREELDR, and installing the latter, and saving the old MBR / boot
38  * sectors in files.
39  */
40 
41 /* FUNCTIONS ****************************************************************/
42 
43 static VOID
TrimTrailingPathSeparators_UStr(IN OUT PUNICODE_STRING UnicodeString)44 TrimTrailingPathSeparators_UStr(
45     IN OUT PUNICODE_STRING UnicodeString)
46 {
47     while (UnicodeString->Length >= sizeof(WCHAR) &&
48            UnicodeString->Buffer[UnicodeString->Length / sizeof(WCHAR) - 1] == OBJ_NAME_PATH_SEPARATOR)
49     {
50         UnicodeString->Length -= sizeof(WCHAR);
51     }
52 }
53 
54 
55 static VOID
CreateFreeLoaderReactOSEntries(IN PVOID BootStoreHandle,IN PCWSTR ArcPath)56 CreateFreeLoaderReactOSEntries(
57     IN PVOID BootStoreHandle,
58     IN PCWSTR ArcPath)
59 {
60     UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)];
61     PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
62     PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
63     BOOT_STORE_OPTIONS BootOptions;
64 
65     BootEntry->Version = FreeLdr;
66     BootEntry->BootFilePath = NULL;
67 
68     BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS);
69     RtlCopyMemory(Options->Signature,
70                   NTOS_OPTIONS_SIGNATURE,
71                   RTL_FIELD_SIZE(NTOS_OPTIONS, Signature));
72 
73     Options->OsLoadPath = ArcPath;
74 
75     /* ReactOS */
76     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS");
77     BootEntry->FriendlyName = L"\"ReactOS\"";
78     Options->OsLoadOptions  = L"/FASTDETECT";
79     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS"));
80 
81     /* ReactOS_Debug */
82     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_Debug");
83     BootEntry->FriendlyName = L"\"ReactOS (Debug)\"";
84     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS";
85     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_Debug"));
86 
87 #ifdef _WINKD_
88     /* ReactOS_VBoxDebug */
89     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_VBoxDebug");
90     BootEntry->FriendlyName = L"\"ReactOS (VBox Debug)\"";
91     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=VBOX /SOS";
92     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_VBoxDebug"));
93 #endif
94 #if DBG
95 #ifndef _WINKD_
96     /* ReactOS_KdSerial */
97     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_KdSerial");
98     BootEntry->FriendlyName = L"\"ReactOS (RosDbg)\"";
99     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS /KDSERIAL";
100     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_KdSerial"));
101 #endif
102 
103     /* ReactOS_Screen */
104     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_Screen");
105     BootEntry->FriendlyName = L"\"ReactOS (Screen)\"";
106     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=SCREEN /SOS";
107     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_Screen"));
108 
109     /* ReactOS_LogFile */
110     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_LogFile");
111     BootEntry->FriendlyName = L"\"ReactOS (Log file)\"";
112     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=FILE /SOS";
113     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_LogFile"));
114 
115     /* ReactOS_Ram */
116     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_Ram");
117     BootEntry->FriendlyName = L"\"ReactOS (RAM Disk)\"";
118     Options->OsLoadPath     = L"ramdisk(0)\\ReactOS";
119     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS /RDPATH=reactos.img /RDIMAGEOFFSET=32256";
120     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_Ram"));
121 
122     /* ReactOS_EMS */
123     // BootEntry->BootEntryKey = MAKESTRKEY(L"ReactOS_EMS");
124     BootEntry->FriendlyName = L"\"ReactOS (Emergency Management Services)\"";
125     Options->OsLoadPath     = ArcPath;
126     Options->OsLoadOptions  = L"/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS /redirect=com2 /redirectbaudrate=115200";
127     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(L"ReactOS_EMS"));
128 #endif
129 
130 
131     /* DefaultOS=ReactOS */
132 #if DBG && !defined(_WINKD_)
133     if (IsUnattendedSetup)
134     {
135         BootOptions.NextBootEntryKey = MAKESTRKEY(L"ReactOS_KdSerial");
136     }
137     else
138 #endif
139     {
140 #if DBG
141         BootOptions.NextBootEntryKey = MAKESTRKEY(L"ReactOS_Debug");
142 #else
143         BootOptions.NextBootEntryKey = MAKESTRKEY(L"ReactOS");
144 #endif
145     }
146 
147 #if DBG
148     if (IsUnattendedSetup)
149 #endif
150     {
151         /* Timeout=0 for unattended or non debug */
152         BootOptions.Timeout = 0;
153     }
154 #if DBG
155     else
156     {
157         /* Timeout=10 */
158         BootOptions.Timeout = 10;
159     }
160 #endif
161 
162     SetBootStoreOptions(BootStoreHandle, &BootOptions,
163                         BOOT_OPTIONS_TIMEOUT | BOOT_OPTIONS_NEXT_BOOTENTRY_KEY);
164 }
165 
166 static NTSTATUS
CreateFreeLoaderIniForReactOS(IN PCWSTR IniPath,IN PCWSTR ArcPath)167 CreateFreeLoaderIniForReactOS(
168     IN PCWSTR IniPath,
169     IN PCWSTR ArcPath)
170 {
171     NTSTATUS Status;
172     PVOID BootStoreHandle;
173 
174     /* Initialize the INI file and create the common FreeLdr sections */
175     Status = OpenBootStore(&BootStoreHandle, IniPath, FreeLdr,
176                            BS_CreateAlways /* BS_OpenAlways */, BS_ReadWriteAccess);
177     if (!NT_SUCCESS(Status))
178         return Status;
179 
180     /* Add the ReactOS entries */
181     CreateFreeLoaderReactOSEntries(BootStoreHandle, ArcPath);
182 
183     /* Close the INI file */
184     CloseBootStore(BootStoreHandle);
185     return STATUS_SUCCESS;
186 }
187 
188 static NTSTATUS
CreateFreeLoaderIniForReactOSAndBootSector(IN PCWSTR IniPath,IN PCWSTR ArcPath,IN PCWSTR Section,IN PCWSTR Description,IN PCWSTR BootPath,IN PCWSTR BootSector)189 CreateFreeLoaderIniForReactOSAndBootSector(
190     IN PCWSTR IniPath,
191     IN PCWSTR ArcPath,
192     IN PCWSTR Section,
193     IN PCWSTR Description,
194     IN PCWSTR BootPath,
195     IN PCWSTR BootSector)
196 {
197     NTSTATUS Status;
198     PVOID BootStoreHandle;
199     UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(BOOTSECTOR_OPTIONS)];
200     PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
201     PBOOTSECTOR_OPTIONS Options = (PBOOTSECTOR_OPTIONS)&BootEntry->OsOptions;
202     WCHAR BootPathBuffer[MAX_PATH] = L"";
203 
204     /* Since the BootPath given here is in NT format
205      * (not ARC), we need to hack-generate a mapping */
206     ULONG DiskNumber = 0, PartitionNumber = 0;
207     PCWSTR PathComponent = NULL;
208 
209     /* From the NT path, compute the disk, partition and path components */
210     // NOTE: this function doesn't support stuff like \Device\FloppyX ...
211     if (NtPathToDiskPartComponents(BootPath, &DiskNumber, &PartitionNumber, &PathComponent))
212     {
213         DPRINT1("BootPath = '%S' points to disk #%d, partition #%d, path '%S'\n",
214                BootPath, DiskNumber, PartitionNumber, PathComponent);
215 
216         /* HACK-build a possible ARC path:
217          * Hard disk path: multi(0)disk(0)rdisk(x)partition(y)[\path] */
218         RtlStringCchPrintfW(BootPathBuffer, _countof(BootPathBuffer),
219                             L"multi(0)disk(0)rdisk(%lu)partition(%lu)",
220                             DiskNumber, PartitionNumber);
221         if (PathComponent && *PathComponent &&
222             (PathComponent[0] != L'\\' || PathComponent[1]))
223         {
224             RtlStringCchCatW(BootPathBuffer, _countof(BootPathBuffer),
225                              PathComponent);
226         }
227     }
228     else
229     {
230         PCWSTR Path = BootPath;
231 
232         if ((_wcsnicmp(Path, L"\\Device\\Floppy", 14) == 0) &&
233             (Path += 14) && iswdigit(*Path))
234         {
235             DiskNumber = wcstoul(Path, (PWSTR*)&PathComponent, 10);
236             if (PathComponent && *PathComponent && *PathComponent != L'\\')
237                 PathComponent = NULL;
238 
239             /* HACK-build a possible ARC path:
240              * Floppy disk path: multi(0)disk(0)fdisk(x)[\path] */
241             RtlStringCchPrintfW(BootPathBuffer, _countof(BootPathBuffer),
242                                 L"multi(0)disk(0)fdisk(%lu)", DiskNumber);
243             if (PathComponent && *PathComponent &&
244                 (PathComponent[0] != L'\\' || PathComponent[1]))
245             {
246                 RtlStringCchCatW(BootPathBuffer, _countof(BootPathBuffer),
247                                  PathComponent);
248             }
249         }
250         else
251         {
252             /* HACK: Just keep the unresolved NT path and hope for the best... */
253 
254             /* Remove any trailing backslash if needed */
255             UNICODE_STRING RootPartition;
256             RtlInitUnicodeString(&RootPartition, BootPath);
257             TrimTrailingPathSeparators_UStr(&RootPartition);
258 
259             /* RootPartition is BootPath without counting any trailing
260              * path separator. Because of this, we need to copy the string
261              * in the buffer, instead of just using a pointer to it. */
262             RtlStringCchPrintfW(BootPathBuffer, _countof(BootPathBuffer),
263                                 L"%wZ", &RootPartition);
264 
265             DPRINT1("Unhandled NT path '%S'\n", BootPath);
266         }
267     }
268 
269     /* Initialize the INI file and create the common FreeLdr sections */
270     Status = OpenBootStore(&BootStoreHandle, IniPath, FreeLdr,
271                            BS_CreateAlways /* BS_OpenAlways */, BS_ReadWriteAccess);
272     if (!NT_SUCCESS(Status))
273         return Status;
274 
275     /* Add the ReactOS entries */
276     CreateFreeLoaderReactOSEntries(BootStoreHandle, ArcPath);
277 
278     BootEntry->Version = FreeLdr;
279     BootEntry->BootFilePath = NULL;
280 
281     BootEntry->OsOptionsLength = sizeof(BOOTSECTOR_OPTIONS);
282     RtlCopyMemory(Options->Signature,
283                   BOOTSECTOR_OPTIONS_SIGNATURE,
284                   RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature));
285 
286     Options->BootPath = BootPathBuffer;
287     Options->FileName = BootSector;
288 
289     // BootEntry->BootEntryKey = MAKESTRKEY(Section);
290     BootEntry->FriendlyName = Description;
291     AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(Section));
292 
293     /* Close the INI file */
294     CloseBootStore(BootStoreHandle);
295     return STATUS_SUCCESS;
296 }
297 
298 //
299 // I think this function can be generalizable as:
300 // "find the corresponding 'ReactOS' boot entry in this loader config file
301 // (here abstraction comes there), and if none, add a new one".
302 //
303 
304 typedef struct _ENUM_REACTOS_ENTRIES_DATA
305 {
306     ULONG i;
307     BOOLEAN UseExistingEntry;
308     PCWSTR ArcPath;
309     WCHAR SectionName[80];
310     WCHAR OsName[80];
311 } ENUM_REACTOS_ENTRIES_DATA, *PENUM_REACTOS_ENTRIES_DATA;
312 
313 // PENUM_BOOT_ENTRIES_ROUTINE
314 static NTSTATUS
315 NTAPI
EnumerateReactOSEntries(IN BOOT_STORE_TYPE Type,IN PBOOT_STORE_ENTRY BootEntry,IN PVOID Parameter OPTIONAL)316 EnumerateReactOSEntries(
317     IN BOOT_STORE_TYPE Type,
318     IN PBOOT_STORE_ENTRY BootEntry,
319     IN PVOID Parameter OPTIONAL)
320 {
321     NTSTATUS Status;
322     PENUM_REACTOS_ENTRIES_DATA Data = (PENUM_REACTOS_ENTRIES_DATA)Parameter;
323     PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
324     WCHAR SystemPath[MAX_PATH];
325 
326     /* We have a boot entry */
327 
328     /* Check for supported boot type "Windows2003" */
329     if (BootEntry->OsOptionsLength < sizeof(NTOS_OPTIONS) ||
330         RtlCompareMemory(&BootEntry->OsOptions /* Signature */,
331                          NTOS_OPTIONS_SIGNATURE,
332                          RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) !=
333                          RTL_FIELD_SIZE(NTOS_OPTIONS, Signature))
334     {
335         /* This is not a ReactOS entry */
336         // DPRINT("    An installation '%S' of unsupported type '%S'\n",
337                // BootEntry->FriendlyName, BootEntry->Version ? BootEntry->Version : L"n/a");
338         DPRINT("    An installation '%S' of unsupported type %lu\n",
339                BootEntry->FriendlyName, BootEntry->OsOptionsLength);
340         /* Continue the enumeration */
341         goto SkipThisEntry;
342     }
343 
344     /* BootType is Windows2003, now check OsLoadPath */
345     if (!Options->OsLoadPath || !*Options->OsLoadPath)
346     {
347         /* Certainly not a ReactOS installation */
348         DPRINT1("    A Win2k3 install '%S' without an ARC path?!\n", BootEntry->FriendlyName);
349         /* Continue the enumeration */
350         goto SkipThisEntry;
351     }
352 
353     if (_wcsicmp(Options->OsLoadPath, Data->ArcPath) != 0)
354     {
355         /* Not found, retry with a quoted path */
356         Status = RtlStringCchPrintfW(SystemPath, ARRAYSIZE(SystemPath), L"\"%s\"", Data->ArcPath);
357         if (!NT_SUCCESS(Status) || _wcsicmp(Options->OsLoadPath, SystemPath) != 0)
358         {
359             /*
360              * This entry is a ReactOS entry, but the SystemRoot
361              * does not match the one we are looking for.
362              */
363             /* Continue the enumeration */
364             goto SkipThisEntry;
365         }
366     }
367 
368     DPRINT("    Found a candidate Win2k3 install '%S' with ARC path '%S'\n",
369            BootEntry->FriendlyName, Options->OsLoadPath);
370     // DPRINT("    Found a Win2k3 install '%S' with ARC path '%S'\n",
371            // BootEntry->FriendlyName, Options->OsLoadPath);
372 
373     DPRINT("EnumerateReactOSEntries: OsLoadPath: '%S'\n", Options->OsLoadPath);
374 
375     Data->UseExistingEntry = TRUE;
376     RtlStringCchCopyW(Data->OsName, ARRAYSIZE(Data->OsName), BootEntry->FriendlyName);
377 
378     /* We have found our entry, stop the enumeration now! */
379     return STATUS_NO_MORE_ENTRIES;
380 
381 SkipThisEntry:
382     Data->UseExistingEntry = FALSE;
383     if (Type == FreeLdr && wcscmp(Data->SectionName, (PWSTR)BootEntry->BootEntryKey)== 0)
384     {
385         RtlStringCchPrintfW(Data->SectionName, ARRAYSIZE(Data->SectionName),
386                             L"ReactOS_%lu", Data->i);
387         RtlStringCchPrintfW(Data->OsName, ARRAYSIZE(Data->OsName),
388                             L"\"ReactOS %lu\"", Data->i);
389         Data->i++;
390     }
391     return STATUS_SUCCESS;
392 }
393 
394 static
395 NTSTATUS
UpdateFreeLoaderIni(IN PCWSTR IniPath,IN PCWSTR ArcPath)396 UpdateFreeLoaderIni(
397     IN PCWSTR IniPath,
398     IN PCWSTR ArcPath)
399 {
400     NTSTATUS Status;
401     PVOID BootStoreHandle;
402     ENUM_REACTOS_ENTRIES_DATA Data;
403     UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)];
404     PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
405     PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
406 
407     /* Open the INI file */
408     Status = OpenBootStore(&BootStoreHandle, IniPath, FreeLdr,
409                            BS_OpenExisting /* BS_OpenAlways */, BS_ReadWriteAccess);
410     if (!NT_SUCCESS(Status))
411         return Status;
412 
413     /* Find an existing usable or an unused section name */
414     Data.UseExistingEntry = TRUE;
415     Data.i = 1;
416     Data.ArcPath = ArcPath;
417     RtlStringCchCopyW(Data.SectionName, ARRAYSIZE(Data.SectionName), L"ReactOS");
418     RtlStringCchCopyW(Data.OsName, ARRAYSIZE(Data.OsName), L"\"ReactOS\"");
419 
420     //
421     // FIXME: We temporarily use EnumerateBootStoreEntries, until
422     // both QueryBootStoreEntry and ModifyBootStoreEntry get implemented.
423     //
424     Status = EnumerateBootStoreEntries(BootStoreHandle, EnumerateReactOSEntries, &Data);
425 
426     /* Create a new "ReactOS" entry if there is none already existing that suits us */
427     if (!Data.UseExistingEntry)
428     {
429         // RtlStringCchPrintfW(Data.SectionName, ARRAYSIZE(Data.SectionName), L"ReactOS_%lu", Data.i);
430         // RtlStringCchPrintfW(Data.OsName, ARRAYSIZE(Data.OsName), L"\"ReactOS %lu\"", Data.i);
431 
432         BootEntry->Version = FreeLdr;
433         BootEntry->BootFilePath = NULL;
434 
435         BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS);
436         RtlCopyMemory(Options->Signature,
437                       NTOS_OPTIONS_SIGNATURE,
438                       RTL_FIELD_SIZE(NTOS_OPTIONS, Signature));
439 
440         Options->OsLoadPath = ArcPath;
441 
442         // BootEntry->BootEntryKey = MAKESTRKEY(Data.SectionName);
443         BootEntry->FriendlyName = Data.OsName;
444         Options->OsLoadOptions  = NULL; // L"";
445         AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(Data.SectionName));
446     }
447 
448     /* Close the INI file */
449     CloseBootStore(BootStoreHandle);
450     return STATUS_SUCCESS;
451 }
452 
453 static
454 NTSTATUS
UpdateBootIni(IN PCWSTR IniPath,IN PCWSTR EntryName,IN PCWSTR EntryValue)455 UpdateBootIni(
456     IN PCWSTR IniPath,
457     IN PCWSTR EntryName,    // ~= ArcPath
458     IN PCWSTR EntryValue)
459 {
460     NTSTATUS Status;
461     PVOID BootStoreHandle;
462     ENUM_REACTOS_ENTRIES_DATA Data;
463 
464     // NOTE: Technically it would be "BootSector"...
465     UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)];
466     PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
467     PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
468 
469     /* Open the INI file */
470     Status = OpenBootStore(&BootStoreHandle, IniPath, NtLdr,
471                            BS_OpenExisting /* BS_OpenAlways */, BS_ReadWriteAccess);
472     if (!NT_SUCCESS(Status))
473         return Status;
474 
475     /* Find an existing usable or an unused section name */
476     Data.UseExistingEntry = TRUE;
477     // Data.i = 1;
478     Data.ArcPath = EntryName;
479     // RtlStringCchCopyW(Data.SectionName, ARRAYSIZE(Data.SectionName), L"ReactOS");
480     RtlStringCchCopyW(Data.OsName, ARRAYSIZE(Data.OsName), L"\"ReactOS\"");
481 
482     //
483     // FIXME: We temporarily use EnumerateBootStoreEntries, until
484     // both QueryBootStoreEntry and ModifyBootStoreEntry get implemented.
485     //
486     Status = EnumerateBootStoreEntries(BootStoreHandle, EnumerateReactOSEntries, &Data);
487 
488     /* If either the key was not found, or contains something else, add a new one */
489     if (!Data.UseExistingEntry /* ||
490         ( (Status == STATUS_NO_MORE_ENTRIES) && wcscmp(Data.OsName, EntryValue) ) */)
491     {
492         BootEntry->Version = NtLdr;
493         BootEntry->BootFilePath = NULL;
494 
495         BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS);
496         RtlCopyMemory(Options->Signature,
497                       NTOS_OPTIONS_SIGNATURE,
498                       RTL_FIELD_SIZE(NTOS_OPTIONS, Signature));
499 
500         Options->OsLoadPath = EntryName;
501 
502         // BootEntry->BootEntryKey = MAKESTRKEY(Data.SectionName);
503         // BootEntry->FriendlyName = Data.OsName;
504         BootEntry->FriendlyName = EntryValue;
505         Options->OsLoadOptions  = NULL; // L"";
506         AddBootStoreEntry(BootStoreHandle, BootEntry, MAKESTRKEY(0 /*Data.SectionName*/));
507     }
508 
509     /* Close the INI file */
510     CloseBootStore(BootStoreHandle);
511     return STATUS_SUCCESS; // Status;
512 }
513 
514 
515 static
516 BOOLEAN
IsThereAValidBootSector(IN PCWSTR RootPath)517 IsThereAValidBootSector(
518     IN PCWSTR RootPath)
519 {
520     /*
521      * We first demand that the bootsector has a valid signature at its end.
522      * We then check the first 3 bytes (as a ULONG) of the bootsector for a
523      * potential "valid" instruction (the BIOS starts execution of the bootsector
524      * at its beginning). Currently this criterium is that this ULONG must be
525      * non-zero. If both these tests pass, then the bootsector is valid; otherwise
526      * it is invalid and certainly needs to be overwritten.
527      */
528 
529     BOOLEAN IsValid = FALSE;
530     NTSTATUS Status;
531     UNICODE_STRING RootPartition;
532     BOOTCODE BootSector = {0};
533 
534     /* Allocate and read the root partition bootsector.
535      * Remove any trailing backslash if needed. */
536     RtlInitUnicodeString(&RootPartition, RootPath);
537     TrimTrailingPathSeparators_UStr(&RootPartition);
538     Status = ReadBootCodeFromFile(&BootSector, &RootPartition, SECTORSIZE);
539     if (!NT_SUCCESS(Status))
540         return FALSE;
541 
542     /* Check for the existence of the bootsector signature */
543     IsValid = (*(PUSHORT)((PUCHAR)BootSector.BootCode + 0x1FE) == 0xAA55);
544     if (IsValid)
545     {
546         /* Check for the first instruction encoded on three bytes */
547         IsValid = (((*(PULONG)BootSector.BootCode) & 0x00FFFFFF) != 0x00000000);
548     }
549 
550     /* Free the bootsector and return */
551     FreeBootCode(&BootSector);
552     return IsValid;
553 }
554 
555 static
556 NTSTATUS
SaveBootSector(IN PCWSTR RootPath,IN PCWSTR DstPath,IN ULONG Length)557 SaveBootSector(
558     IN PCWSTR RootPath,
559     IN PCWSTR DstPath,
560     IN ULONG Length)
561 {
562     NTSTATUS Status;
563     UNICODE_STRING Name;
564     OBJECT_ATTRIBUTES ObjectAttributes;
565     IO_STATUS_BLOCK IoStatusBlock;
566     HANDLE FileHandle;
567     // LARGE_INTEGER FileOffset;
568     BOOTCODE BootSector = {0};
569 
570     /* Allocate and read the root partition bootsector.
571      * Remove any trailing backslash if needed. */
572     RtlInitUnicodeString(&Name, RootPath);
573     TrimTrailingPathSeparators_UStr(&Name);
574     Status = ReadBootCodeFromFile(&BootSector, &Name, Length);
575     if (!NT_SUCCESS(Status))
576         return Status;
577 
578     /* Write the bootsector to DstPath */
579     RtlInitUnicodeString(&Name, DstPath);
580     InitializeObjectAttributes(&ObjectAttributes,
581                                &Name,
582                                OBJ_CASE_INSENSITIVE,
583                                NULL,
584                                NULL);
585 
586     Status = NtCreateFile(&FileHandle,
587                           GENERIC_WRITE | SYNCHRONIZE,
588                           &ObjectAttributes,
589                           &IoStatusBlock,
590                           NULL,
591                           FILE_ATTRIBUTE_NORMAL,
592                           0,
593                           FILE_SUPERSEDE,
594                           FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
595                           NULL,
596                           0);
597     if (!NT_SUCCESS(Status))
598     {
599         FreeBootCode(&BootSector);
600         return Status;
601     }
602 
603     Status = NtWriteFile(FileHandle,
604                          NULL,
605                          NULL,
606                          NULL,
607                          &IoStatusBlock,
608                          BootSector.BootCode,
609                          BootSector.Length,
610                          NULL,
611                          NULL);
612     NtClose(FileHandle);
613 
614     /* Free the bootsector and return */
615     FreeBootCode(&BootSector);
616     return Status;
617 }
618 
619 
620 static
621 NTSTATUS
InstallBootCodeToDisk(IN PCWSTR SrcPath,IN PCWSTR RootPath,IN PFS_INSTALL_BOOTCODE InstallBootCode)622 InstallBootCodeToDisk(
623     IN PCWSTR SrcPath,
624     IN PCWSTR RootPath,
625     IN PFS_INSTALL_BOOTCODE InstallBootCode)
626 {
627     NTSTATUS Status, LockStatus;
628     UNICODE_STRING Name;
629     OBJECT_ATTRIBUTES ObjectAttributes;
630     IO_STATUS_BLOCK IoStatusBlock;
631     HANDLE PartitionHandle;
632 
633     /*
634      * Open the root partition from which the bootcode (MBR, VBR) parameters
635      * will be obtained; this is also where we will write the updated bootcode.
636      * Remove any trailing backslash if needed.
637      */
638     RtlInitUnicodeString(&Name, RootPath);
639     TrimTrailingPathSeparators_UStr(&Name);
640 
641     InitializeObjectAttributes(&ObjectAttributes,
642                                &Name,
643                                OBJ_CASE_INSENSITIVE,
644                                NULL,
645                                NULL);
646 
647     Status = NtOpenFile(&PartitionHandle,
648                         GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
649                         &ObjectAttributes,
650                         &IoStatusBlock,
651                         FILE_SHARE_READ | FILE_SHARE_WRITE,
652                         FILE_SYNCHRONOUS_IO_NONALERT /* | FILE_SEQUENTIAL_ONLY */);
653     if (!NT_SUCCESS(Status))
654         return Status;
655 
656     /* Lock the volume */
657     LockStatus = NtFsControlFile(PartitionHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0);
658     if (!NT_SUCCESS(LockStatus))
659     {
660         DPRINT1("Unable to lock the volume before installing boot code. Status 0x%08x. Expect problems.\n", LockStatus);
661     }
662 
663     /* Install the bootcode (MBR, VBR) */
664     Status = InstallBootCode(SrcPath, PartitionHandle, PartitionHandle);
665 
666     /* dismount & Unlock the volume */
667     if (NT_SUCCESS(LockStatus))
668     {
669         LockStatus = NtFsControlFile(PartitionHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0);
670         if (!NT_SUCCESS(LockStatus))
671         {
672             DPRINT1("Unable to dismount the volume after installing boot code. Status 0x%08x. Expect problems.\n", LockStatus);
673         }
674 
675         LockStatus = NtFsControlFile(PartitionHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0);
676         if (!NT_SUCCESS(LockStatus))
677         {
678             DPRINT1("Unable to unlock the volume after installing boot code. Status 0x%08x. Expect problems.\n", LockStatus);
679         }
680     }
681 
682     /* Close the partition */
683     NtClose(PartitionHandle);
684 
685     return Status;
686 }
687 
688 static
689 NTSTATUS
InstallBootCodeToFile(IN PCWSTR SrcPath,IN PCWSTR DstPath,IN PCWSTR RootPath,IN PFS_INSTALL_BOOTCODE InstallBootCode)690 InstallBootCodeToFile(
691     IN PCWSTR SrcPath,
692     IN PCWSTR DstPath,
693     IN PCWSTR RootPath,
694     IN PFS_INSTALL_BOOTCODE InstallBootCode)
695 {
696     NTSTATUS Status;
697     UNICODE_STRING Name;
698     OBJECT_ATTRIBUTES ObjectAttributes;
699     IO_STATUS_BLOCK IoStatusBlock;
700     HANDLE PartitionHandle, FileHandle;
701 
702     /*
703      * Open the root partition from which the bootcode (MBR, VBR)
704      * parameters will be obtained.
705      *
706      * FIXME? It might be possible that we need to also open it for writing
707      * access in case we really need to still write the second portion of
708      * the boot sector ????
709      *
710      * Remove any trailing backslash if needed.
711      */
712     RtlInitUnicodeString(&Name, RootPath);
713     TrimTrailingPathSeparators_UStr(&Name);
714 
715     InitializeObjectAttributes(&ObjectAttributes,
716                                &Name,
717                                OBJ_CASE_INSENSITIVE,
718                                NULL,
719                                NULL);
720 
721     Status = NtOpenFile(&PartitionHandle,
722                         GENERIC_READ | SYNCHRONIZE,
723                         &ObjectAttributes,
724                         &IoStatusBlock,
725                         FILE_SHARE_READ | FILE_SHARE_WRITE,
726                         FILE_SYNCHRONOUS_IO_NONALERT /* | FILE_SEQUENTIAL_ONLY */);
727     if (!NT_SUCCESS(Status))
728         return Status;
729 
730     /* Open or create the file where the new bootsector will be saved */
731     RtlInitUnicodeString(&Name, DstPath);
732     InitializeObjectAttributes(&ObjectAttributes,
733                                &Name,
734                                OBJ_CASE_INSENSITIVE,
735                                NULL,
736                                NULL);
737 
738     Status = NtCreateFile(&FileHandle,
739                           GENERIC_WRITE | SYNCHRONIZE,
740                           &ObjectAttributes,
741                           &IoStatusBlock,
742                           NULL,
743                           FILE_ATTRIBUTE_NORMAL,
744                           0,
745                           FILE_SUPERSEDE, // FILE_OVERWRITE_IF
746                           FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
747                           NULL,
748                           0);
749     if (!NT_SUCCESS(Status))
750     {
751         DPRINT1("NtCreateFile() failed (Status %lx)\n", Status);
752         NtClose(PartitionHandle);
753         return Status;
754     }
755 
756     /* Install the bootcode (MBR, VBR) */
757     Status = InstallBootCode(SrcPath, FileHandle, PartitionHandle);
758 
759     /* Close the file and the partition */
760     NtClose(FileHandle);
761     NtClose(PartitionHandle);
762 
763     return Status;
764 }
765 
766 
767 static
768 NTSTATUS
InstallMbrBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE DiskHandle)769 InstallMbrBootCode(
770     IN PCWSTR SrcPath,      // MBR source file (on the installation medium)
771     IN HANDLE DstPath,      // Where to save the bootsector built from the source + disk information
772     IN HANDLE DiskHandle)   // Disk holding the (old) MBR information
773 {
774     NTSTATUS Status;
775     UNICODE_STRING Name;
776     IO_STATUS_BLOCK IoStatusBlock;
777     LARGE_INTEGER FileOffset;
778     BOOTCODE OrigBootSector = {0};
779     BOOTCODE NewBootSector  = {0};
780 
781 C_ASSERT(sizeof(PARTITION_SECTOR) == SECTORSIZE);
782 
783     /* Allocate and read the current original MBR bootsector */
784     Status = ReadBootCodeByHandle(&OrigBootSector,
785                                   DiskHandle,
786                                   sizeof(PARTITION_SECTOR));
787     if (!NT_SUCCESS(Status))
788         return Status;
789 
790     /* Allocate and read the new bootsector from SrcPath */
791     RtlInitUnicodeString(&Name, SrcPath);
792     Status = ReadBootCodeFromFile(&NewBootSector,
793                                   &Name,
794                                   sizeof(PARTITION_SECTOR));
795     if (!NT_SUCCESS(Status))
796     {
797         FreeBootCode(&OrigBootSector);
798         return Status;
799     }
800 
801     /*
802      * Copy the disk signature, the reserved fields and
803      * the partition table from the old MBR to the new one.
804      */
805     RtlCopyMemory(&((PPARTITION_SECTOR)NewBootSector.BootCode)->Signature,
806                   &((PPARTITION_SECTOR)OrigBootSector.BootCode)->Signature,
807                   sizeof(PARTITION_SECTOR) -
808                   FIELD_OFFSET(PARTITION_SECTOR, Signature)
809                   /* Length of partition table */);
810 
811     /* Free the original bootsector */
812     FreeBootCode(&OrigBootSector);
813 
814     /* Write the new bootsector to DstPath */
815     FileOffset.QuadPart = 0ULL;
816     Status = NtWriteFile(DstPath,
817                          NULL,
818                          NULL,
819                          NULL,
820                          &IoStatusBlock,
821                          NewBootSector.BootCode,
822                          NewBootSector.Length,
823                          &FileOffset,
824                          NULL);
825 
826     /* Free the new bootsector */
827     FreeBootCode(&NewBootSector);
828 
829     return Status;
830 }
831 
832 static
833 NTSTATUS
InstallMbrBootCodeToDisk(_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCWSTR DestinationDevicePathBuffer)834 InstallMbrBootCodeToDisk(
835     _In_ PCUNICODE_STRING SystemRootPath,
836     _In_ PCUNICODE_STRING SourceRootPath,
837     _In_ PCWSTR DestinationDevicePathBuffer)
838 {
839     NTSTATUS Status;
840     WCHAR SourceMbrPathBuffer[MAX_PATH];
841     WCHAR DstPath[MAX_PATH];
842 
843 #if 0
844     /*
845      * The DestinationDevicePathBuffer parameter has been built with
846      * the following instruction by the caller; I'm not yet sure whether
847      * I actually want this function to build the path instead, hence
848      * I keep this code here but disabled for now...
849      */
850     WCHAR DestinationDevicePathBuffer[MAX_PATH];
851     RtlStringCchPrintfW(DestinationDevicePathBuffer, ARRAYSIZE(DestinationDevicePathBuffer),
852                         L"\\Device\\Harddisk%d\\Partition0",
853                         DiskNumber);
854 #endif
855 
856     CombinePaths(SourceMbrPathBuffer, ARRAYSIZE(SourceMbrPathBuffer), 2,
857                  SourceRootPath->Buffer, L"\\loader\\dosmbr.bin");
858 
859     if (IsThereAValidBootSector(DestinationDevicePathBuffer))
860     {
861         /* Save current MBR */
862         CombinePaths(DstPath, ARRAYSIZE(DstPath), 2,
863                      SystemRootPath->Buffer, L"mbr.old");
864 
865         DPRINT1("Save MBR: %S ==> %S\n", DestinationDevicePathBuffer, DstPath);
866         Status = SaveBootSector(DestinationDevicePathBuffer, DstPath, sizeof(PARTITION_SECTOR));
867         if (!NT_SUCCESS(Status))
868         {
869             DPRINT1("SaveBootSector() failed (Status %lx)\n", Status);
870             // Don't care if we succeeded or not saving the old MBR, just go ahead.
871         }
872     }
873 
874     DPRINT1("Install MBR bootcode: %S ==> %S\n",
875             SourceMbrPathBuffer, DestinationDevicePathBuffer);
876 
877     /* Install the MBR */
878     return InstallBootCodeToDisk(SourceMbrPathBuffer,
879                                  DestinationDevicePathBuffer,
880                                  InstallMbrBootCode);
881 }
882 
883 
884 static
885 NTSTATUS
InstallBootloaderFiles(_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath)886 InstallBootloaderFiles(
887     _In_ PCUNICODE_STRING SystemRootPath,
888     _In_ PCUNICODE_STRING SourceRootPath)
889 {
890     NTSTATUS Status;
891     WCHAR SrcPath[MAX_PATH];
892     WCHAR DstPath[MAX_PATH];
893 
894     /* Copy FreeLoader to the system partition, always overwriting the older version */
895     CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\freeldr.sys");
896     CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath->Buffer, L"freeldr.sys");
897 
898     DPRINT1("Copy: %S ==> %S\n", SrcPath, DstPath);
899     Status = SetupCopyFile(SrcPath, DstPath, FALSE);
900     if (!NT_SUCCESS(Status))
901     {
902         DPRINT1("SetupCopyFile() failed (Status 0x%08lx)\n", Status);
903         return Status;
904     }
905 
906     return STATUS_SUCCESS;
907 }
908 
909 static
910 NTSTATUS
InstallFatBootcodeToPartition(_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath,_In_ PCWSTR FileSystemName)911 InstallFatBootcodeToPartition(
912     _In_ PCUNICODE_STRING SystemRootPath,
913     _In_ PCUNICODE_STRING SourceRootPath,
914     _In_ PCUNICODE_STRING DestinationArcPath,
915     _In_ PCWSTR FileSystemName)
916 {
917     NTSTATUS Status;
918     BOOLEAN DoesFreeLdrExist;
919     WCHAR SrcPath[MAX_PATH];
920     WCHAR DstPath[MAX_PATH];
921 
922     /* FAT or FAT32 partition */
923     DPRINT("System path: '%wZ'\n", SystemRootPath);
924 
925     /* Install the bootloader */
926     Status = InstallBootloaderFiles(SystemRootPath, SourceRootPath);
927     if (!NT_SUCCESS(Status))
928     {
929         DPRINT1("InstallBootloaderFiles() failed (Status %lx)\n", Status);
930         return Status;
931     }
932 
933     /* Prepare for possibly updating 'freeldr.ini' */
934     DoesFreeLdrExist = DoesFileExist_2(SystemRootPath->Buffer, L"freeldr.ini");
935     if (DoesFreeLdrExist)
936     {
937         /* Update existing 'freeldr.ini' */
938         DPRINT1("Update existing 'freeldr.ini'\n");
939         Status = UpdateFreeLoaderIni(SystemRootPath->Buffer, DestinationArcPath->Buffer);
940         if (!NT_SUCCESS(Status))
941         {
942             DPRINT1("UpdateFreeLoaderIni() failed (Status %lx)\n", Status);
943             return Status;
944         }
945     }
946 
947     /* Check for NT and other bootloaders */
948 
949     // FIXME: Check for Vista+ bootloader!
950     /*** Status = FindBootStore(PartitionHandle, NtLdr, &Version); ***/
951     /*** Status = FindBootStore(PartitionHandle, BootMgr, &Version); ***/
952     if (DoesFileExist_2(SystemRootPath->Buffer, L"NTLDR") == TRUE ||
953         DoesFileExist_2(SystemRootPath->Buffer, L"BOOT.INI") == TRUE)
954     {
955         /* Search root directory for 'NTLDR' and 'BOOT.INI' */
956         DPRINT1("Found Microsoft Windows NT/2000/XP boot loader\n");
957 
958         /* Create or update 'freeldr.ini' */
959         if (DoesFreeLdrExist == FALSE)
960         {
961             /* Create new 'freeldr.ini' */
962             DPRINT1("Create new 'freeldr.ini'\n");
963             Status = CreateFreeLoaderIniForReactOS(SystemRootPath->Buffer, DestinationArcPath->Buffer);
964             if (!NT_SUCCESS(Status))
965             {
966                 DPRINT1("CreateFreeLoaderIniForReactOS() failed (Status %lx)\n", Status);
967                 return Status;
968             }
969 
970             /* Install new bootcode into a file */
971             CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath->Buffer, L"bootsect.ros");
972 
973             if (_wcsicmp(FileSystemName, L"FAT32") == 0)
974             {
975                 /* Install FAT32 bootcode */
976                 CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\fat32.bin");
977 
978                 DPRINT1("Install FAT32 bootcode: %S ==> %S\n", SrcPath, DstPath);
979                 Status = InstallBootCodeToFile(SrcPath, DstPath,
980                                                SystemRootPath->Buffer,
981                                                InstallFat32BootCode);
982                 if (!NT_SUCCESS(Status))
983                 {
984                     DPRINT1("InstallBootCodeToFile(FAT32) failed (Status %lx)\n", Status);
985                     return Status;
986                 }
987             }
988             else // if (wcsicmp(FileSystemName, L"FAT") == 0)
989             {
990                 /* Install FAT16 bootcode */
991                 CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\fat.bin");
992 
993                 DPRINT1("Install FAT16 bootcode: %S ==> %S\n", SrcPath, DstPath);
994                 Status = InstallBootCodeToFile(SrcPath, DstPath,
995                                                SystemRootPath->Buffer,
996                                                InstallFat16BootCode);
997                 if (!NT_SUCCESS(Status))
998                 {
999                     DPRINT1("InstallBootCodeToFile(FAT16) failed (Status %lx)\n", Status);
1000                     return Status;
1001                 }
1002             }
1003         }
1004 
1005         /* Update 'boot.ini' */
1006         /* Windows' NTLDR loads an external bootsector file when the specified drive
1007            letter is C:, otherwise it will interpret it as a boot DOS path specifier. */
1008         DPRINT1("Update 'boot.ini'\n");
1009         Status = UpdateBootIni(SystemRootPath->Buffer,
1010                                L"C:\\bootsect.ros",
1011                                L"\"ReactOS\"");
1012         if (!NT_SUCCESS(Status))
1013         {
1014             DPRINT1("UpdateBootIni() failed (Status %lx)\n", Status);
1015             return Status;
1016         }
1017     }
1018     else
1019     {
1020         /* Non-NT bootloaders: install our own bootloader */
1021 
1022         PCWSTR Section;
1023         PCWSTR Description;
1024         PCWSTR BootSector;
1025 
1026         /* Search for COMPAQ MS-DOS 1.x (1.11, 1.12, based on MS-DOS 1.25) boot loader */
1027         if (DoesFileExist_2(SystemRootPath->Buffer, L"IOSYS.COM") == TRUE ||
1028             DoesFileExist_2(SystemRootPath->Buffer, L"MSDOS.COM") == TRUE)
1029         {
1030             DPRINT1("Found COMPAQ MS-DOS 1.x (1.11, 1.12) / MS-DOS 1.25 boot loader\n");
1031 
1032             Section     = L"CPQDOS";
1033             Description = L"\"COMPAQ MS-DOS 1.x / MS-DOS 1.25\"";
1034             BootSector  = L"BOOTSECT.DOS";
1035         }
1036         else
1037         /* Search for Microsoft DOS or Windows 9x boot loader */
1038         if (DoesFileExist_2(SystemRootPath->Buffer, L"IO.SYS") == TRUE ||
1039             DoesFileExist_2(SystemRootPath->Buffer, L"MSDOS.SYS") == TRUE)
1040             // WINBOOT.SYS
1041         {
1042             DPRINT1("Found Microsoft DOS or Windows 9x boot loader\n");
1043 
1044             Section     = L"MSDOS";
1045             Description = L"\"MS-DOS/Windows\"";
1046             BootSector  = L"BOOTSECT.DOS";
1047         }
1048         else
1049         /* Search for IBM PC-DOS or DR-DOS 5.x boot loader */
1050         if (DoesFileExist_2(SystemRootPath->Buffer, L"IBMIO.COM" ) == TRUE || // Some people refer to this file instead of IBMBIO.COM...
1051             DoesFileExist_2(SystemRootPath->Buffer, L"IBMBIO.COM") == TRUE ||
1052             DoesFileExist_2(SystemRootPath->Buffer, L"IBMDOS.COM") == TRUE)
1053         {
1054             DPRINT1("Found IBM PC-DOS or DR-DOS 5.x or IBM OS/2 1.0\n");
1055 
1056             Section     = L"IBMDOS";
1057             Description = L"\"IBM PC-DOS or DR-DOS 5.x or IBM OS/2 1.0\"";
1058             BootSector  = L"BOOTSECT.DOS";
1059         }
1060         else
1061         /* Search for DR-DOS 3.x boot loader */
1062         if (DoesFileExist_2(SystemRootPath->Buffer, L"DRBIOS.SYS") == TRUE ||
1063             DoesFileExist_2(SystemRootPath->Buffer, L"DRBDOS.SYS") == TRUE)
1064         {
1065             DPRINT1("Found DR-DOS 3.x\n");
1066 
1067             Section     = L"DRDOS";
1068             Description = L"\"DR-DOS 3.x\"";
1069             BootSector  = L"BOOTSECT.DOS";
1070         }
1071         else
1072         /* Search for Dell Real-Mode Kernel (DRMK) OS */
1073         if (DoesFileExist_2(SystemRootPath->Buffer, L"DELLBIO.BIN") == TRUE ||
1074             DoesFileExist_2(SystemRootPath->Buffer, L"DELLRMK.BIN") == TRUE)
1075         {
1076             DPRINT1("Found Dell Real-Mode Kernel OS\n");
1077 
1078             Section     = L"DRMK";
1079             Description = L"\"Dell Real-Mode Kernel OS\"";
1080             BootSector  = L"BOOTSECT.DOS";
1081         }
1082         else
1083         /* Search for MS OS/2 1.x */
1084         if (DoesFileExist_2(SystemRootPath->Buffer, L"OS2BOOT.COM") == TRUE ||
1085             DoesFileExist_2(SystemRootPath->Buffer, L"OS2BIO.COM" ) == TRUE ||
1086             DoesFileExist_2(SystemRootPath->Buffer, L"OS2DOS.COM" ) == TRUE)
1087         {
1088             DPRINT1("Found MS OS/2 1.x\n");
1089 
1090             Section     = L"MSOS2";
1091             Description = L"\"MS OS/2 1.x\"";
1092             BootSector  = L"BOOTSECT.OS2";
1093         }
1094         else
1095         /* Search for MS or IBM OS/2 */
1096         if (DoesFileExist_2(SystemRootPath->Buffer, L"OS2BOOT") == TRUE ||
1097             DoesFileExist_2(SystemRootPath->Buffer, L"OS2LDR" ) == TRUE ||
1098             DoesFileExist_2(SystemRootPath->Buffer, L"OS2KRNL") == TRUE)
1099         {
1100             DPRINT1("Found MS/IBM OS/2\n");
1101 
1102             Section     = L"IBMOS2";
1103             Description = L"\"MS/IBM OS/2\"";
1104             BootSector  = L"BOOTSECT.OS2";
1105         }
1106         else
1107         /* Search for FreeDOS boot loader */
1108         if (DoesFileExist_2(SystemRootPath->Buffer, L"kernel.sys") == TRUE)
1109         {
1110             DPRINT1("Found FreeDOS boot loader\n");
1111 
1112             Section     = L"FDOS";
1113             Description = L"\"FreeDOS\"";
1114             BootSector  = L"BOOTSECT.DOS";
1115         }
1116         else
1117         {
1118             /* No or unknown boot loader */
1119             DPRINT1("No or unknown boot loader found\n");
1120 
1121             Section     = L"Unknown";
1122             Description = L"\"Unknown Operating System\"";
1123             BootSector  = L"BOOTSECT.OLD";
1124         }
1125 
1126         /* Create or update 'freeldr.ini' */
1127         if (DoesFreeLdrExist == FALSE)
1128         {
1129             /* Create new 'freeldr.ini' */
1130             DPRINT1("Create new 'freeldr.ini'\n");
1131 
1132             if (IsThereAValidBootSector(SystemRootPath->Buffer))
1133             {
1134                 Status = CreateFreeLoaderIniForReactOSAndBootSector(
1135                              SystemRootPath->Buffer, DestinationArcPath->Buffer,
1136                              Section, Description,
1137                              SystemRootPath->Buffer, BootSector);
1138                 if (!NT_SUCCESS(Status))
1139                 {
1140                     DPRINT1("CreateFreeLoaderIniForReactOSAndBootSector() failed (Status %lx)\n", Status);
1141                     return Status;
1142                 }
1143 
1144                 /* Save current bootsector */
1145                 CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath->Buffer, BootSector);
1146 
1147                 DPRINT1("Save bootsector: %S ==> %S\n", SystemRootPath->Buffer, DstPath);
1148                 Status = SaveBootSector(SystemRootPath->Buffer, DstPath, SECTORSIZE);
1149                 if (!NT_SUCCESS(Status))
1150                 {
1151                     DPRINT1("SaveBootSector() failed (Status %lx)\n", Status);
1152                     return Status;
1153                 }
1154             }
1155             else
1156             {
1157                 Status = CreateFreeLoaderIniForReactOS(SystemRootPath->Buffer, DestinationArcPath->Buffer);
1158                 if (!NT_SUCCESS(Status))
1159                 {
1160                     DPRINT1("CreateFreeLoaderIniForReactOS() failed (Status %lx)\n", Status);
1161                     return Status;
1162                 }
1163             }
1164 
1165             /* Install new bootsector on the disk */
1166             if (_wcsicmp(FileSystemName, L"FAT32") == 0)
1167             {
1168                 /* Install FAT32 bootcode */
1169                 CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\fat32.bin");
1170 
1171                 DPRINT1("Install FAT32 bootcode: %S ==> %S\n", SrcPath, SystemRootPath->Buffer);
1172                 Status = InstallBootCodeToDisk(SrcPath, SystemRootPath->Buffer, InstallFat32BootCode);
1173                 DPRINT1("Status: 0x%08X\n", Status);
1174                 if (!NT_SUCCESS(Status))
1175                 {
1176                     DPRINT1("InstallBootCodeToDisk(FAT32) failed (Status %lx)\n", Status);
1177                     return Status;
1178                 }
1179             }
1180             else // if (wcsicmp(FileSystemName, L"FAT") == 0)
1181             {
1182                 /* Install FAT16 bootcode */
1183                 CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\fat.bin");
1184 
1185                 DPRINT1("Install FAT16 bootcode: %S ==> %S\n", SrcPath, SystemRootPath->Buffer);
1186                 Status = InstallBootCodeToDisk(SrcPath, SystemRootPath->Buffer, InstallFat16BootCode);
1187                 if (!NT_SUCCESS(Status))
1188                 {
1189                     DPRINT1("InstallBootCodeToDisk(FAT16) failed (Status %lx)\n", Status);
1190                     return Status;
1191                 }
1192             }
1193         }
1194     }
1195 
1196     return STATUS_SUCCESS;
1197 }
1198 
1199 static
1200 NTSTATUS
InstallBtrfsBootcodeToPartition(_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath)1201 InstallBtrfsBootcodeToPartition(
1202     _In_ PCUNICODE_STRING SystemRootPath,
1203     _In_ PCUNICODE_STRING SourceRootPath,
1204     _In_ PCUNICODE_STRING DestinationArcPath)
1205 {
1206     NTSTATUS Status;
1207     BOOLEAN DoesFreeLdrExist;
1208     WCHAR SrcPath[MAX_PATH];
1209     WCHAR DstPath[MAX_PATH];
1210 
1211     /* BTRFS partition */
1212     DPRINT("System path: '%wZ'\n", SystemRootPath);
1213 
1214     /* Install the bootloader */
1215     Status = InstallBootloaderFiles(SystemRootPath, SourceRootPath);
1216     if (!NT_SUCCESS(Status))
1217     {
1218         DPRINT1("InstallBootloaderFiles() failed (Status %lx)\n", Status);
1219         return Status;
1220     }
1221 
1222     /* Prepare for possibly updating 'freeldr.ini' */
1223     DoesFreeLdrExist = DoesFileExist_2(SystemRootPath->Buffer, L"freeldr.ini");
1224     if (DoesFreeLdrExist)
1225     {
1226         /* Update existing 'freeldr.ini' */
1227         DPRINT1("Update existing 'freeldr.ini'\n");
1228         Status = UpdateFreeLoaderIni(SystemRootPath->Buffer, DestinationArcPath->Buffer);
1229         if (!NT_SUCCESS(Status))
1230         {
1231             DPRINT1("UpdateFreeLoaderIni() failed (Status %lx)\n", Status);
1232             return Status;
1233         }
1234     }
1235 
1236     /* Check for *nix bootloaders */
1237 
1238     /* Create or update 'freeldr.ini' */
1239     if (DoesFreeLdrExist == FALSE)
1240     {
1241         /* Create new 'freeldr.ini' */
1242         DPRINT1("Create new 'freeldr.ini'\n");
1243 
1244         /* Certainly SysLinux, GRUB, LILO... or an unknown boot loader */
1245         DPRINT1("*nix or unknown boot loader found\n");
1246 
1247         if (IsThereAValidBootSector(SystemRootPath->Buffer))
1248         {
1249             PCWSTR BootSector = L"BOOTSECT.OLD";
1250 
1251             Status = CreateFreeLoaderIniForReactOSAndBootSector(
1252                          SystemRootPath->Buffer, DestinationArcPath->Buffer,
1253                          L"Linux", L"\"Linux\"",
1254                          SystemRootPath->Buffer, BootSector);
1255             if (!NT_SUCCESS(Status))
1256             {
1257                 DPRINT1("CreateFreeLoaderIniForReactOSAndBootSector() failed (Status %lx)\n", Status);
1258                 return Status;
1259             }
1260 
1261             /* Save current bootsector */
1262             CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath->Buffer, BootSector);
1263 
1264             DPRINT1("Save bootsector: %S ==> %S\n", SystemRootPath->Buffer, DstPath);
1265             Status = SaveBootSector(SystemRootPath->Buffer, DstPath, BTRFS_BOOTSECTOR_SIZE);
1266             if (!NT_SUCCESS(Status))
1267             {
1268                 DPRINT1("SaveBootSector() failed (Status %lx)\n", Status);
1269                 return Status;
1270             }
1271         }
1272         else
1273         {
1274             Status = CreateFreeLoaderIniForReactOS(SystemRootPath->Buffer, DestinationArcPath->Buffer);
1275             if (!NT_SUCCESS(Status))
1276             {
1277                 DPRINT1("CreateFreeLoaderIniForReactOS() failed (Status %lx)\n", Status);
1278                 return Status;
1279             }
1280         }
1281 
1282         /* Install new bootsector on the disk */
1283         /* Install BTRFS bootcode */
1284         CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\btrfs.bin");
1285 
1286         DPRINT1("Install BTRFS bootcode: %S ==> %S\n", SrcPath, SystemRootPath->Buffer);
1287         Status = InstallBootCodeToDisk(SrcPath, SystemRootPath->Buffer, InstallBtrfsBootCode);
1288         if (!NT_SUCCESS(Status))
1289         {
1290             DPRINT1("InstallBootCodeToDisk(BTRFS) failed (Status %lx)\n", Status);
1291             return Status;
1292         }
1293     }
1294 
1295     return STATUS_SUCCESS;
1296 }
1297 
1298 static
1299 NTSTATUS
InstallNtfsBootcodeToPartition(_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath)1300 InstallNtfsBootcodeToPartition(
1301     _In_ PCUNICODE_STRING SystemRootPath,
1302     _In_ PCUNICODE_STRING SourceRootPath,
1303     _In_ PCUNICODE_STRING DestinationArcPath)
1304 {
1305     NTSTATUS Status;
1306     BOOLEAN DoesFreeLdrExist;
1307     WCHAR SrcPath[MAX_PATH];
1308     WCHAR DstPath[MAX_PATH];
1309 
1310     /* NTFS partition */
1311     DPRINT("System path: '%wZ'\n", SystemRootPath);
1312 
1313     /* Install the bootloader */
1314     Status = InstallBootloaderFiles(SystemRootPath, SourceRootPath);
1315     if (!NT_SUCCESS(Status))
1316     {
1317         DPRINT1("InstallBootloaderFiles() failed (Status %lx)\n", Status);
1318         return Status;
1319     }
1320 
1321     /* Prepare for possibly updating 'freeldr.ini' */
1322     DoesFreeLdrExist = DoesFileExist_2(SystemRootPath->Buffer, L"freeldr.ini");
1323     if (DoesFreeLdrExist)
1324     {
1325         /* Update existing 'freeldr.ini' */
1326         DPRINT1("Update existing 'freeldr.ini'\n");
1327         Status = UpdateFreeLoaderIni(SystemRootPath->Buffer, DestinationArcPath->Buffer);
1328         if (!NT_SUCCESS(Status))
1329         {
1330             DPRINT1("UpdateFreeLoaderIni() failed (Status %lx)\n", Status);
1331             return Status;
1332         }
1333 
1334         return STATUS_SUCCESS;
1335     }
1336 
1337     /* Check for *nix bootloaders */
1338 
1339     DPRINT1("Create new 'freeldr.ini'\n");
1340 
1341     /* Certainly SysLinux, GRUB, LILO... or an unknown boot loader */
1342     DPRINT1("*nix or unknown boot loader found\n");
1343 
1344     if (IsThereAValidBootSector(SystemRootPath->Buffer))
1345     {
1346         PCWSTR BootSector = L"BOOTSECT.OLD";
1347 
1348         Status = CreateFreeLoaderIniForReactOSAndBootSector(
1349                      SystemRootPath->Buffer, DestinationArcPath->Buffer,
1350                      L"Linux", L"\"Linux\"",
1351                      SystemRootPath->Buffer, BootSector);
1352         if (!NT_SUCCESS(Status))
1353         {
1354             DPRINT1("CreateFreeLoaderIniForReactOSAndBootSector() failed (Status %lx)\n", Status);
1355             return Status;
1356         }
1357 
1358         /* Save current bootsector */
1359         CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath->Buffer, BootSector);
1360 
1361         DPRINT1("Save bootsector: %S ==> %S\n", SystemRootPath->Buffer, DstPath);
1362         Status = SaveBootSector(SystemRootPath->Buffer, DstPath, NTFS_BOOTSECTOR_SIZE);
1363         if (!NT_SUCCESS(Status))
1364         {
1365             DPRINT1("SaveBootSector() failed (Status %lx)\n", Status);
1366             return Status;
1367         }
1368     }
1369     else
1370     {
1371         Status = CreateFreeLoaderIniForReactOS(SystemRootPath->Buffer, DestinationArcPath->Buffer);
1372         if (!NT_SUCCESS(Status))
1373         {
1374             DPRINT1("CreateFreeLoaderIniForReactOS() failed (Status %lx)\n", Status);
1375             return Status;
1376         }
1377     }
1378 
1379     /* Install new bootsector on the disk */
1380 
1381     /* Install NTFS bootcode */
1382     CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\ntfs.bin");
1383 
1384     DPRINT1("Install NTFS bootcode: %S ==> %S\n", SrcPath, SystemRootPath->Buffer);
1385     Status = InstallBootCodeToDisk(SrcPath, SystemRootPath->Buffer, InstallNtfsBootCode);
1386     if (!NT_SUCCESS(Status))
1387     {
1388         DPRINT1("InstallBootCodeToDisk(NTFS) failed (Status %lx)\n", Status);
1389         return Status;
1390     }
1391 
1392     return STATUS_SUCCESS;
1393 }
1394 
1395 static
1396 NTSTATUS
InstallVBRToPartition(_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath,_In_ PCWSTR FileSystemName)1397 InstallVBRToPartition(
1398     _In_ PCUNICODE_STRING SystemRootPath,
1399     _In_ PCUNICODE_STRING SourceRootPath,
1400     _In_ PCUNICODE_STRING DestinationArcPath,
1401     _In_ PCWSTR FileSystemName)
1402 {
1403     if (_wcsicmp(FileSystemName, L"FAT")   == 0 ||
1404         _wcsicmp(FileSystemName, L"FAT32") == 0)
1405     {
1406         return InstallFatBootcodeToPartition(SystemRootPath,
1407                                              SourceRootPath,
1408                                              DestinationArcPath,
1409                                              FileSystemName);
1410     }
1411     else if (_wcsicmp(FileSystemName, L"NTFS") == 0)
1412     {
1413         return InstallNtfsBootcodeToPartition(SystemRootPath,
1414                                               SourceRootPath,
1415                                               DestinationArcPath);
1416     }
1417     else if (_wcsicmp(FileSystemName, L"BTRFS") == 0)
1418     {
1419         return InstallBtrfsBootcodeToPartition(SystemRootPath,
1420                                                SourceRootPath,
1421                                                DestinationArcPath);
1422     }
1423     /*
1424     else if (_wcsicmp(FileSystemName, L"EXT2")  == 0 ||
1425              _wcsicmp(FileSystemName, L"EXT3")  == 0 ||
1426              _wcsicmp(FileSystemName, L"EXT4")  == 0)
1427     {
1428         return STATUS_NOT_SUPPORTED;
1429     }
1430     */
1431     else
1432     {
1433         /* Unknown file system */
1434         DPRINT1("Unknown file system '%S'\n", FileSystemName);
1435     }
1436 
1437     return STATUS_NOT_SUPPORTED;
1438 }
1439 
1440 
1441 /* GENERIC FUNCTIONS *********************************************************/
1442 
1443 /**
1444  * @brief
1445  * Helper for InstallBootManagerAndBootEntries().
1446  *
1447  * @param[in]   ArchType
1448  * @param[in]   SystemRootPath
1449  * See InstallBootManagerAndBootEntries() parameters.
1450  *
1451  * @param[in]   DiskNumber
1452  * The NT disk number of the system disk that contains the system partition.
1453  *
1454  * @param[in]   DiskStyle
1455  * The partitioning style of the system disk.
1456  *
1457  * @param[in]   IsSuperFloppy
1458  * Whether the system disk is a super-floppy.
1459  *
1460  * @param[in]   FileSystem
1461  * The file system of the system partition.
1462  *
1463  * @param[in]   SourceRootPath
1464  * @param[in]   DestinationArcPath
1465  * @param[in]   Options
1466  * See InstallBootManagerAndBootEntries() parameters.
1467  *
1468  * @return  An NTSTATUS code indicating success or failure.
1469  **/
1470 static
1471 NTSTATUS
InstallBootManagerAndBootEntriesWorker(_In_ ARCHITECTURE_TYPE ArchType,_In_ PCUNICODE_STRING SystemRootPath,_In_ ULONG DiskNumber,_In_ PARTITION_STYLE DiskStyle,_In_ BOOLEAN IsSuperFloppy,_In_ PCWSTR FileSystem,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath,_In_ ULONG_PTR Options)1472 InstallBootManagerAndBootEntriesWorker(
1473     _In_ ARCHITECTURE_TYPE ArchType,
1474     _In_ PCUNICODE_STRING SystemRootPath,
1475     _In_ ULONG DiskNumber, // const STORAGE_DEVICE_NUMBER* DeviceNumber,
1476     _In_ PARTITION_STYLE DiskStyle,
1477     _In_ BOOLEAN IsSuperFloppy,
1478     _In_ PCWSTR FileSystem,
1479     _In_ PCUNICODE_STRING SourceRootPath,
1480     _In_ PCUNICODE_STRING DestinationArcPath,
1481     _In_ ULONG_PTR Options)
1482 {
1483     NTSTATUS Status;
1484     BOOLEAN IsBIOS = ((ArchType == ARCH_PcAT) || (ArchType == ARCH_NEC98x86));
1485     UCHAR InstallType = (Options & 0x03);
1486 
1487     // FIXME: We currently only support BIOS-based PCs
1488     // TODO: Support other platforms
1489     if (!IsBIOS)
1490         return STATUS_NOT_SUPPORTED;
1491 
1492     if (InstallType <= 1)
1493     {
1494         /* Step 1: Write the VBR */
1495         Status = InstallVBRToPartition(SystemRootPath,
1496                                        SourceRootPath,
1497                                        DestinationArcPath,
1498                                        FileSystem);
1499         if (!NT_SUCCESS(Status))
1500         {
1501             DPRINT1("InstallVBRToPartition() failed (Status 0x%08lx)\n", Status);
1502             return ERROR_WRITE_BOOT; // Status; STATUS_BAD_MASTER_BOOT_RECORD;
1503         }
1504 
1505         /* Step 2: Write the MBR if the disk containing the
1506          * system partition is MBR and not a super-floppy */
1507         if ((InstallType == 1) && (DiskStyle == PARTITION_STYLE_MBR) && !IsSuperFloppy)
1508         {
1509             WCHAR SystemDiskPath[MAX_PATH];
1510             RtlStringCchPrintfW(SystemDiskPath, _countof(SystemDiskPath),
1511                                 L"\\Device\\Harddisk%d\\Partition0",
1512                                 DiskNumber);
1513             Status = InstallMbrBootCodeToDisk(SystemRootPath,
1514                                               SourceRootPath,
1515                                               SystemDiskPath);
1516             if (!NT_SUCCESS(Status))
1517             {
1518                 DPRINT1("InstallMbrBootCodeToDisk() failed (Status 0x%08lx)\n", Status);
1519                 return ERROR_INSTALL_BOOTCODE; // Status; STATUS_BAD_MASTER_BOOT_RECORD;
1520             }
1521         }
1522     }
1523     else if (InstallType == 2)
1524     {
1525         WCHAR SrcPath[MAX_PATH];
1526 
1527         // FIXME: We currently only support FAT12 file system.
1528         if (_wcsicmp(FileSystem, L"FAT") != 0)
1529             return STATUS_NOT_SUPPORTED;
1530 
1531         // TODO: In the future, we'll be able to use InstallVBRToPartition()
1532         // directly, instead of re-doing manually the copy steps below.
1533 
1534         /* Install the bootloader to the boot partition */
1535         Status = InstallBootloaderFiles(SystemRootPath, SourceRootPath);
1536         if (!NT_SUCCESS(Status))
1537         {
1538             DPRINT1("InstallBootloaderFiles() failed (Status 0x%08lx)\n", Status);
1539             return Status;
1540         }
1541 
1542         /* Create new 'freeldr.ini' */
1543         DPRINT("Create new 'freeldr.ini'\n");
1544         Status = CreateFreeLoaderIniForReactOS(SystemRootPath->Buffer, DestinationArcPath->Buffer);
1545         if (!NT_SUCCESS(Status))
1546         {
1547             DPRINT1("CreateFreeLoaderIniForReactOS() failed (Status 0x%08lx)\n", Status);
1548             return Status;
1549         }
1550 
1551         /* Install FAT12 bootsector */
1552         CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 2, SourceRootPath->Buffer, L"\\loader\\fat.bin");
1553 
1554         DPRINT1("Install FAT12 bootcode: %S ==> %S\n", SrcPath, SystemRootPath->Buffer);
1555         Status = InstallBootCodeToDisk(SrcPath, SystemRootPath->Buffer, InstallFat12BootCode);
1556         if (!NT_SUCCESS(Status))
1557         {
1558             DPRINT1("InstallBootCodeToDisk(FAT12) failed (Status 0x%08lx)\n", Status);
1559             return Status;
1560         }
1561     }
1562 
1563     return Status;
1564 }
1565 
1566 
1567 NTSTATUS
GetDeviceInfo_UStr(_In_opt_ PCUNICODE_STRING DeviceName,_In_opt_ HANDLE DeviceHandle,_Out_ PFILE_FS_DEVICE_INFORMATION DeviceInfo)1568 GetDeviceInfo_UStr(
1569     _In_opt_ PCUNICODE_STRING DeviceName,
1570     _In_opt_ HANDLE DeviceHandle,
1571     _Out_ PFILE_FS_DEVICE_INFORMATION DeviceInfo)
1572 {
1573     NTSTATUS Status;
1574     IO_STATUS_BLOCK IoStatusBlock;
1575 
1576     if (DeviceName && DeviceHandle)
1577         return STATUS_INVALID_PARAMETER_MIX;
1578 
1579     /* Open the device if a name has been given;
1580      * otherwise just use the provided handle. */
1581     if (DeviceName)
1582     {
1583         Status = pOpenDeviceEx_UStr(DeviceName, &DeviceHandle,
1584                                     FILE_READ_ATTRIBUTES,
1585                                     FILE_SHARE_READ | FILE_SHARE_WRITE);
1586         if (!NT_SUCCESS(Status))
1587         {
1588             DPRINT1("Cannot open device '%wZ' (Status 0x%08lx)\n",
1589                     DeviceName, Status);
1590             return Status;
1591         }
1592     }
1593 
1594     /* Query the device */
1595     Status = NtQueryVolumeInformationFile(DeviceHandle,
1596                                           &IoStatusBlock,
1597                                           DeviceInfo,
1598                                           sizeof(*DeviceInfo),
1599                                           FileFsDeviceInformation);
1600     if (!NT_SUCCESS(Status))
1601         DPRINT1("FileFsDeviceInformation failed (Status 0x%08lx)\n", Status);
1602 
1603     /* Close the device if we've opened it */
1604     if (DeviceName)
1605         NtClose(DeviceHandle);
1606 
1607     return Status;
1608 }
1609 
1610 NTSTATUS
GetDeviceInfo(_In_opt_ PCWSTR DeviceName,_In_opt_ HANDLE DeviceHandle,_Out_ PFILE_FS_DEVICE_INFORMATION DeviceInfo)1611 GetDeviceInfo(
1612     _In_opt_ PCWSTR DeviceName,
1613     _In_opt_ HANDLE DeviceHandle,
1614     _Out_ PFILE_FS_DEVICE_INFORMATION DeviceInfo)
1615 {
1616     UNICODE_STRING DeviceNameU;
1617 
1618     if (DeviceName && DeviceHandle)
1619         return STATUS_INVALID_PARAMETER_MIX;
1620 
1621     if (DeviceName)
1622         RtlInitUnicodeString(&DeviceNameU, DeviceName);
1623 
1624     return GetDeviceInfo_UStr(DeviceName ? &DeviceNameU : NULL,
1625                               DeviceName ? NULL : DeviceHandle,
1626                               DeviceInfo);
1627 }
1628 
1629 
1630 /**
1631  * @brief
1632  * Installs FreeLoader on the system and configure the boot entries.
1633  *
1634  * @todo
1635  * Split this function into just the InstallBootManager, and a separate one
1636  * for just the boot entries.
1637  *
1638  * @param[in]   ArchType
1639  * The target architecture.
1640  *
1641  * @param[in]   SystemRootPath
1642  * The system partition path, where the FreeLdr boot manager and its
1643  * settings are saved to.
1644  *
1645  * @param[in]   SourceRootPath
1646  * The installation source, where to copy the FreeLdr boot manager from.
1647  *
1648  * @param[in]   DestinationArcPath
1649  * The ReactOS installation path in ARC format.
1650  *
1651  * @param[in]   Options
1652  * For BIOS-based PCs:
1653  * LOBYTE:
1654  *      0: Install only on VBR;
1655  *      1: Install on both VBR and MBR.
1656  *      2: Install on removable disk.
1657  *
1658  * @return  An NTSTATUS code indicating success or failure.
1659  **/
1660 NTSTATUS
InstallBootManagerAndBootEntries(_In_ ARCHITECTURE_TYPE ArchType,_In_ PCUNICODE_STRING SystemRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath,_In_ ULONG_PTR Options)1661 InstallBootManagerAndBootEntries(
1662     _In_ ARCHITECTURE_TYPE ArchType,
1663     _In_ PCUNICODE_STRING SystemRootPath,
1664     _In_ PCUNICODE_STRING SourceRootPath,
1665     _In_ PCUNICODE_STRING DestinationArcPath,
1666     _In_ ULONG_PTR Options)
1667 {
1668     NTSTATUS Status;
1669     HANDLE DeviceHandle;
1670     FILE_FS_DEVICE_INFORMATION DeviceInfo;
1671     ULONG DiskNumber;
1672     PARTITION_STYLE PartitionStyle;
1673     BOOLEAN IsSuperFloppy;
1674     WCHAR FileSystem[MAX_PATH+1];
1675 
1676     /* Remove any trailing backslash if needed */
1677     UNICODE_STRING RootPartition = *SystemRootPath;
1678     TrimTrailingPathSeparators_UStr(&RootPartition);
1679 
1680     /* Open the volume */
1681     Status = pOpenDeviceEx_UStr(&RootPartition, &DeviceHandle,
1682                                 GENERIC_READ,
1683                                 FILE_SHARE_READ | FILE_SHARE_WRITE);
1684     if (!NT_SUCCESS(Status))
1685     {
1686         DPRINT1("Cannot open %wZ for bootloader installation (Status 0x%08lx)\n",
1687                 &RootPartition, Status);
1688         return Status;
1689     }
1690 
1691     /* Retrieve the volume file system (it will also be mounted) */
1692     Status = GetFileSystemName_UStr(NULL, DeviceHandle,
1693                                     FileSystem, sizeof(FileSystem));
1694     if (!NT_SUCCESS(Status) || !*FileSystem)
1695     {
1696         DPRINT1("GetFileSystemName() failed (Status 0x%08lx)\n", Status);
1697         goto Quit;
1698     }
1699 
1700     /* Retrieve the device type and characteristics */
1701     Status = GetDeviceInfo_UStr(NULL, DeviceHandle, &DeviceInfo);
1702     if (!NT_SUCCESS(Status))
1703     {
1704         DPRINT1("FileFsDeviceInformation failed (Status 0x%08lx)\n", Status);
1705         goto Quit;
1706     }
1707 
1708     /* Ignore volumes that are NOT on usual disks */
1709     if (DeviceInfo.DeviceType != FILE_DEVICE_DISK /*&&
1710         DeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK*/)
1711     {
1712         DPRINT1("Invalid volume; device type %lu\n", DeviceInfo.DeviceType);
1713         Status = STATUS_INVALID_DEVICE_REQUEST;
1714         goto Quit;
1715     }
1716 
1717 
1718     /* Check whether this is a floppy or a partitionable device */
1719     if (DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE)
1720     {
1721         /* Floppies don't have partitions */
1722         // NOTE: See ntoskrnl/io/iomgr/rawfs.c!RawQueryFsSizeInfo()
1723         DiskNumber = ULONG_MAX;
1724         PartitionStyle = PARTITION_STYLE_MBR;
1725         IsSuperFloppy = TRUE;
1726     }
1727     else
1728     {
1729         IO_STATUS_BLOCK IoStatusBlock;
1730         STORAGE_DEVICE_NUMBER DeviceNumber;
1731 
1732         /* The maximum information a DISK_GEOMETRY_EX dynamic structure can contain */
1733         typedef struct _DISK_GEOMETRY_EX_INTERNAL
1734         {
1735             DISK_GEOMETRY Geometry;
1736             LARGE_INTEGER DiskSize;
1737             DISK_PARTITION_INFO Partition;
1738             /* Followed by: DISK_DETECTION_INFO Detection; unused here */
1739         } DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL;
1740 
1741         DISK_GEOMETRY_EX_INTERNAL DiskGeoEx;
1742         PARTITION_INFORMATION PartitionInfo;
1743 
1744         /* Retrieve the disk number. NOTE: Fails for floppy disks. */
1745         Status = NtDeviceIoControlFile(DeviceHandle,
1746                                        NULL, NULL, NULL,
1747                                        &IoStatusBlock,
1748                                        IOCTL_STORAGE_GET_DEVICE_NUMBER,
1749                                        NULL, 0,
1750                                        &DeviceNumber, sizeof(DeviceNumber));
1751         if (!NT_SUCCESS(Status))
1752             goto Quit; /* This may be a dynamic volume, which is unsupported */
1753         ASSERT(DeviceNumber.DeviceType == DeviceInfo.DeviceType);
1754         if (DeviceNumber.DeviceNumber == ULONG_MAX)
1755         {
1756             DPRINT1("Invalid disk number reported, bail out\n");
1757             Status = STATUS_NOT_FOUND;
1758             goto Quit;
1759         }
1760 
1761         /* Retrieve the drive geometry. NOTE: Fails for floppy disks;
1762          * use IOCTL_DISK_GET_DRIVE_GEOMETRY instead. */
1763         Status = NtDeviceIoControlFile(DeviceHandle,
1764                                        NULL, NULL, NULL,
1765                                        &IoStatusBlock,
1766                                        IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
1767                                        NULL, 0,
1768                                        &DiskGeoEx,
1769                                        sizeof(DiskGeoEx));
1770         if (!NT_SUCCESS(Status))
1771         {
1772             DPRINT1("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX failed (Status 0x%08lx)\n", Status);
1773             goto Quit;
1774         }
1775 
1776         /*
1777          * Retrieve the volume's partition information.
1778          * NOTE: Fails for floppy disks.
1779          *
1780          * NOTE: We can use the non-EX IOCTL because the super-floppy test will
1781          * fail anyway if the disk is NOT MBR-partitioned. (If the disk is GPT,
1782          * the IOCTL would return only the MBR protective partition, but the
1783          * super-floppy test would fail due to the wrong partitioning style.)
1784          */
1785         Status = NtDeviceIoControlFile(DeviceHandle,
1786                                        NULL, NULL, NULL,
1787                                        &IoStatusBlock,
1788                                        IOCTL_DISK_GET_PARTITION_INFO,
1789                                        NULL, 0,
1790                                        &PartitionInfo,
1791                                        sizeof(PartitionInfo));
1792         if (!NT_SUCCESS(Status))
1793         {
1794             DPRINT1("IOCTL_DISK_GET_PARTITION_INFO failed (Status 0x%08lx)\n", Status);
1795             goto Quit;
1796         }
1797 
1798         DiskNumber = DeviceNumber.DeviceNumber;
1799         PartitionStyle = DiskGeoEx.Partition.PartitionStyle;
1800         IsSuperFloppy = IsDiskSuperFloppy2(&DiskGeoEx.Partition,
1801                                            (PULONGLONG)&DiskGeoEx.DiskSize.QuadPart,
1802                                            &PartitionInfo);
1803     }
1804 
1805     Status = InstallBootManagerAndBootEntriesWorker(
1806                 ArchType, SystemRootPath,
1807                 DiskNumber, PartitionStyle, IsSuperFloppy, FileSystem,
1808                 SourceRootPath, DestinationArcPath, Options);
1809 
1810 Quit:
1811     NtClose(DeviceHandle);
1812     return Status;
1813 }
1814 
1815 NTSTATUS
InstallBootcodeToRemovable(_In_ ARCHITECTURE_TYPE ArchType,_In_ PCUNICODE_STRING RemovableRootPath,_In_ PCUNICODE_STRING SourceRootPath,_In_ PCUNICODE_STRING DestinationArcPath)1816 InstallBootcodeToRemovable(
1817     _In_ ARCHITECTURE_TYPE ArchType,
1818     _In_ PCUNICODE_STRING RemovableRootPath,
1819     _In_ PCUNICODE_STRING SourceRootPath,
1820     _In_ PCUNICODE_STRING DestinationArcPath)
1821 {
1822     NTSTATUS Status;
1823     FILE_FS_DEVICE_INFORMATION DeviceInfo;
1824     PCWSTR FileSystemName;
1825     BOOLEAN IsFloppy;
1826 
1827     /* Remove any trailing backslash if needed */
1828     UNICODE_STRING RootDrive = *RemovableRootPath;
1829     TrimTrailingPathSeparators_UStr(&RootDrive);
1830 
1831     /* Verify that the removable disk is accessible */
1832     if (!DoesDirExist(NULL, RemovableRootPath->Buffer))
1833         return STATUS_DEVICE_NOT_READY;
1834 
1835     /* Retrieve the device type and characteristics */
1836     Status = GetDeviceInfo_UStr(&RootDrive, NULL, &DeviceInfo);
1837     if (!NT_SUCCESS(Status))
1838     {
1839         static const UNICODE_STRING DeviceFloppy = RTL_CONSTANT_STRING(L"\\Device\\Floppy");
1840 
1841         DPRINT1("FileFsDeviceInformation failed (Status 0x%08lx)\n", Status);
1842 
1843         /* Definitively fail if the device is not a floppy */
1844         if (!RtlPrefixUnicodeString(&DeviceFloppy, &RootDrive, TRUE))
1845             return Status; /* We cannot cope with a failure */
1846 
1847         /* Try to fall back to something "sane" if the device may be a floppy */
1848         DeviceInfo.DeviceType = FILE_DEVICE_DISK;
1849         DeviceInfo.Characteristics = FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE;
1850     }
1851 
1852     /* Ignore volumes that are NOT on usual disks */
1853     if (DeviceInfo.DeviceType != FILE_DEVICE_DISK /*&&
1854         DeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK*/)
1855     {
1856         DPRINT1("Invalid volume; device type %lu\n", DeviceInfo.DeviceType);
1857         return STATUS_INVALID_DEVICE_REQUEST;
1858     }
1859 
1860     /* Fail if the disk is not removable */
1861     if (!(DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA))
1862     {
1863         DPRINT1("Device is NOT removable!\n");
1864         return STATUS_INVALID_DEVICE_REQUEST;
1865     }
1866 
1867     /* Check whether this is a floppy or another removable device */
1868     IsFloppy = !!(DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE);
1869 
1870     /* Use FAT32, unless the device is a floppy disk */
1871     FileSystemName = (IsFloppy ? L"FAT" : L"FAT32");
1872 
1873     /* Format the removable disk */
1874     Status = FormatFileSystem_UStr(&RootDrive,
1875                                    FileSystemName,
1876                                    (IsFloppy ? FMIFS_FLOPPY : FMIFS_REMOVABLE),
1877                                    NULL,
1878                                    TRUE,
1879                                    0,
1880                                    NULL);
1881     if (!NT_SUCCESS(Status))
1882     {
1883         if (Status == STATUS_NOT_SUPPORTED)
1884             DPRINT1("%s FS non-existent on this system!\n", FileSystemName);
1885         else
1886             DPRINT1("FormatFileSystem(%s) failed (Status 0x%08lx)\n", FileSystemName, Status);
1887         return Status;
1888     }
1889 
1890     /* Copy FreeLoader to the removable disk and save the boot entries */
1891     Status = InstallBootManagerAndBootEntries(ArchType,
1892                                               RemovableRootPath,
1893                                               SourceRootPath,
1894                                               DestinationArcPath,
1895                                               2 /* Install on removable media */);
1896     if (!NT_SUCCESS(Status))
1897         DPRINT1("InstallBootManagerAndBootEntries() failed (Status 0x%08lx)\n", Status);
1898     return Status;
1899 }
1900 
1901 /* EOF */
1902