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