xref: /reactos/base/setup/lib/utils/bldrsup.c (revision a7cbb60e)
1 /*
2  * PROJECT:     ReactOS Setup Library
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Boot Stores Management functionality, with support for
5  *              NT 5.x family (MS Windows <= 2003, and ReactOS) bootloaders.
6  * COPYRIGHT:   Copyright 2017-2018 Hermes Belusca-Maito
7  */
8 
9 // TODO: Add support for NT 6.x family! (detection + BCD manipulation).
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include "precomp.h"
14 
15 #include "bldrsup.h"
16 #include "filesup.h"
17 #include "inicache.h"
18 
19 #define NDEBUG
20 #include <debug.h>
21 
22 
23 /* GLOBALS ******************************************************************/
24 
25 typedef NTSTATUS
26 (*POPEN_BOOT_STORE)(
27     _Out_ PVOID* Handle,
28     _In_ HANDLE PartitionDirectoryHandle, // _In_opt_
29     _In_ BOOT_STORE_TYPE Type,
30     _In_ BOOT_STORE_OPENMODE OpenMode,
31     _In_ BOOT_STORE_ACCESS Access);
32 
33 typedef NTSTATUS
34 (*PCLOSE_BOOT_STORE)(
35     _In_ PVOID Handle);
36 
37 typedef NTSTATUS
38 (*PENUM_BOOT_STORE_ENTRIES)(
39     IN PVOID Handle,
40 //  IN ULONG Flags, // Determine which data to retrieve
41     IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,
42     IN PVOID Parameter OPTIONAL);
43 
44 typedef struct _NTOS_BOOT_LOADER_FILES
45 {
46     BOOT_STORE_TYPE Type;
47     PCZZWSTR LoaderExecutables;
48     PCWSTR LoaderConfigurationFile;
49     POPEN_BOOT_STORE OpenBootStore;
50     PCLOSE_BOOT_STORE CloseBootStore;
51     PENUM_BOOT_STORE_ENTRIES EnumBootStoreEntries;
52 } NTOS_BOOT_LOADER_FILES, *PNTOS_BOOT_LOADER_FILES;
53 
54 
55 /*
56  * Header for particular store contexts
57  */
58 typedef struct _BOOT_STORE_CONTEXT
59 {
60     BOOT_STORE_TYPE Type;
61     BOOLEAN ReadOnly;
62 //  PNTOS_BOOT_LOADER_FILES ??
63 /*
64     PVOID PrivateData;
65 */
66 } BOOT_STORE_CONTEXT, *PBOOT_STORE_CONTEXT;
67 
68 typedef struct _BOOT_STORE_INI_CONTEXT
69 {
70     BOOT_STORE_CONTEXT Header;
71 
72     /*
73      * If all these members are NULL, we know that the store is freshly created
74      * and is cached in memory only. At file closure we will therefore need to
75      * create the file proper and save its contents.
76      */
77     HANDLE FileHandle;
78     HANDLE SectionHandle;
79     // SIZE_T ViewSize;
80     ULONG FileSize;
81     PVOID ViewBase;
82 
83     PINICACHE IniCache;
84     PINI_SECTION OptionsIniSection;
85     PINI_SECTION OsIniSection;
86 } BOOT_STORE_INI_CONTEXT, *PBOOT_STORE_INI_CONTEXT;
87 
88 // TODO!
89 typedef struct _BOOT_STORE_BCDREG_CONTEXT
90 {
91     BOOT_STORE_CONTEXT Header;
92     ULONG PlaceHolder;
93 } BOOT_STORE_BCDREG_CONTEXT, *PBOOT_STORE_BCDREG_CONTEXT;
94 
95 
96 static NTSTATUS
97 OpenIniBootLoaderStore(
98     _Out_ PVOID* Handle,
99     _In_ HANDLE PartitionDirectoryHandle, // _In_opt_
100     _In_ BOOT_STORE_TYPE Type,
101     _In_ BOOT_STORE_OPENMODE OpenMode,
102     _In_ BOOT_STORE_ACCESS Access);
103 
104 static NTSTATUS
105 CloseIniBootLoaderStore(
106     _In_ PVOID Handle);
107 
108 static NTSTATUS
109 FreeLdrEnumerateBootEntries(
110     IN PBOOT_STORE_INI_CONTEXT BootStore,
111 //  IN ULONG Flags, // Determine which data to retrieve
112     IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,
113     IN PVOID Parameter OPTIONAL);
114 
115 static NTSTATUS
116 NtLdrEnumerateBootEntries(
117     IN PBOOT_STORE_INI_CONTEXT BootStore,
118 //  IN ULONG Flags, // Determine which data to retrieve
119     IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,
120     IN PVOID Parameter OPTIONAL);
121 
122 
123 // Question 1: What if config file is optional?
124 // Question 2: What if many config files are possible?
125 NTOS_BOOT_LOADER_FILES NtosBootLoaders[] =
126 {
127     {FreeLdr, L"freeldr.sys\0", L"freeldr.ini",
128         OpenIniBootLoaderStore, CloseIniBootLoaderStore, (PENUM_BOOT_STORE_ENTRIES)FreeLdrEnumerateBootEntries},
129     {NtLdr  , L"ntldr\0" L"osloader.exe\0", L"boot.ini",
130         OpenIniBootLoaderStore, CloseIniBootLoaderStore, (PENUM_BOOT_STORE_ENTRIES)NtLdrEnumerateBootEntries  },
131 //  {SetupLdr, L"setupldr\0" L"setupldr.bin\0" L"setupldr.exe\0", L"txtsetup.sif", UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED}
132 //  {BootMgr , L"bootmgr", L"BCD", UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED}
133 };
134 C_ASSERT(_countof(NtosBootLoaders) == BldrTypeMax);
135 
136 enum BOOT_OPTION
137 {
138     BO_TimeOut,
139     BO_DefaultOS,
140 };
141 static const PCWSTR BootOptionNames[][2] =
142 {
143     {L"TimeOut", L"DefaultOS"}, // FreeLdr
144     {L"timeout", L"default"  }  // NtLdr
145 };
146 
147 
148 /* FUNCTIONS ****************************************************************/
149 
150 NTSTATUS
FindBootStore(IN HANDLE PartitionDirectoryHandle,IN BOOT_STORE_TYPE Type,OUT PULONG VersionNumber OPTIONAL)151 FindBootStore( // By handle
152     IN HANDLE PartitionDirectoryHandle, // OPTIONAL
153     IN BOOT_STORE_TYPE Type,
154     OUT PULONG VersionNumber OPTIONAL)
155 // OUT PHANDLE ConfigFileHande OPTIONAL ????
156 {
157     PCWSTR LoaderExecutable;
158     // UINT i;
159 
160     if (Type >= BldrTypeMax)
161         return STATUS_INVALID_PARAMETER;
162 
163     if (VersionNumber)
164         *VersionNumber = 0;
165 
166     /* Check whether any of the loader executables exist */
167     LoaderExecutable = NtosBootLoaders[Type].LoaderExecutables;
168     while (*LoaderExecutable)
169     {
170         if (DoesFileExist(PartitionDirectoryHandle, LoaderExecutable))
171         {
172             /* A loader was found, stop there */
173             DPRINT("Found loader executable '%S'\n", LoaderExecutable);
174             break;
175         }
176 
177         /* The loader does not exist, continue with another one */
178         DPRINT("Loader executable '%S' does not exist, continue with another one...\n", LoaderExecutable);
179         LoaderExecutable += wcslen(LoaderExecutable) + 1;
180     }
181     if (!*LoaderExecutable)
182     {
183         /* No loader was found */
184         DPRINT("No loader executable was found\n");
185         return STATUS_NOT_FOUND;
186     }
187 
188     /* Check for loader version if needed */
189     if (VersionNumber)
190     {
191         *VersionNumber = 0;
192         // TODO: Check for BLDR version!
193     }
194 
195     /* Check whether the loader configuration file exists */
196 #if 0
197     Status = OpenAndMapFile(PartitionDirectoryHandle, NtosBootLoaders[Type].LoaderConfigurationFile,
198                             &FileHandle, &FileSize, &SectionHandle, &ViewBase, FALSE);
199     if (!NT_SUCCESS(Status))
200 #else
201     if (!DoesFileExist(PartitionDirectoryHandle, NtosBootLoaders[Type].LoaderConfigurationFile))
202 #endif
203     {
204         /* The loader does not exist, continue with another one */
205         // FIXME: Consider it might be optional??
206         DPRINT1("Loader configuration file '%S' does not exist\n", NtosBootLoaders[Type].LoaderConfigurationFile);
207         return STATUS_NOT_FOUND;
208     }
209 
210     return STATUS_SUCCESS;
211 }
212 
213 
214 //
215 // TEMPORARY functions to migrate the DEPRECATED BootDrive and BootPartition
216 // values of BootSector boot entries in FREELDR.INI to the newer BootPath value.
217 //
218 // REMOVE THEM once they won't be necessary anymore,
219 // after the removal of their support in FreeLoader!
220 //
221 static VOID
FreeLdrMigrateBootDrivePartWorker(_In_ PINI_SECTION OsIniSection)222 FreeLdrMigrateBootDrivePartWorker(
223     _In_ PINI_SECTION OsIniSection)
224 {
225     PCWSTR KeyData;
226     PINI_KEYWORD OldKey;
227 
228     /*
229      * Check whether we have a "BootPath" value (takes precedence
230      * over both "BootDrive" and "BootPartition").
231      */
232     if (IniGetKey(OsIniSection, L"BootPath", &KeyData) && KeyData && *KeyData)
233     {
234         /* We already have a BootPath value, do nothing more */
235         return;
236     }
237 
238     /* We don't have one: retrieve the BIOS drive and
239      * partition and convert them to a valid ARC path */
240 
241     /* Retrieve the boot drive */
242     OldKey = IniGetKey(OsIniSection, L"BootDrive", &KeyData);
243     if (OldKey)
244     {
245         PCWSTR OldDrive = KeyData;
246         ULONG DriveNumber = 0;
247         ULONG PartitionNumber = 0;
248         UCHAR DriveType = 0;
249         WCHAR BufferBootPath[80]; // 80 chars is enough for "multi(0)disk(0)rdisk(x)partition(y)", with (x,y) == MAXULONG
250 
251         /* If a number string is given, then just
252          * convert it to decimal (BIOS HW only) */
253         PCWCH p = KeyData;
254         if (p[0] >= L'0' && p[0] <= L'9')
255         {
256             DriveNumber = wcstoul(p, (PWCHAR*)&p, 0);
257             if (DriveNumber >= 0x80)
258             {
259                 /* It's quite probably a hard disk */
260                 DriveNumber -= 0x80;
261                 DriveType = L'h';
262             }
263             else
264             {
265                 /* It's quite probably a floppy */
266                 DriveType = L'f';
267             }
268         }
269         else if (p[0] && towlower(p[1]) == L'd')
270         {
271             /* Convert the drive number string into a number: 'hd1' = 1 */
272             DriveType = tolower(p[0]);
273             DriveNumber = _wtoi(&p[2]);
274         }
275 
276         /* Retrieve the boot partition (optional, fall back to zero otherwise) */
277         if (IniGetKey(OsIniSection, L"BootPartition", &KeyData))
278             PartitionNumber = _wtoi(KeyData);
279 
280         if (DriveType == L'f')
281         {
282             /* Floppy disk path: multi(0)disk(0)fdisk(x) */
283             RtlStringCchPrintfW(BufferBootPath, _countof(BufferBootPath),
284                                 L"multi(0)disk(0)fdisk(%lu)", DriveNumber);
285         }
286         else if (DriveType == L'h')
287         {
288             /* Hard disk path: multi(0)disk(0)rdisk(x)partition(y) */
289             RtlStringCchPrintfW(BufferBootPath, _countof(BufferBootPath),
290                                 L"multi(0)disk(0)rdisk(%lu)partition(%lu)",
291                                 DriveNumber, PartitionNumber);
292         }
293         else if (DriveType == L'c')
294         {
295             /* CD-ROM disk path: multi(0)disk(0)cdrom(x) */
296             RtlStringCchPrintfW(BufferBootPath, _countof(BufferBootPath),
297                                 L"multi(0)disk(0)cdrom(%lu)", DriveNumber);
298         }
299         else
300         {
301             /* This case should rarely happen, if ever */
302             DPRINT1("Unrecognized BootDrive type '%C'\n", DriveType ? DriveType : L'?');
303 
304             /* Build the boot path in the form: hdX,Y */
305             RtlStringCchCopyW(BufferBootPath, _countof(BufferBootPath), OldDrive);
306             if (KeyData && *KeyData)
307             {
308                 RtlStringCchCatW(BufferBootPath, _countof(BufferBootPath), L",");
309                 RtlStringCchCatW(BufferBootPath, _countof(BufferBootPath), KeyData);
310             }
311         }
312 
313         /* Add the new BootPath value */
314         IniInsertKey(OsIniSection, OldKey, INSERT_BEFORE, L"BootPath", BufferBootPath);
315     }
316 
317     /* Delete the deprecated BootDrive and BootPartition values */
318     IniRemoveKeyByName(OsIniSection, L"BootDrive");
319     IniRemoveKeyByName(OsIniSection, L"BootPartition");
320 }
321 
322 static VOID
FreeLdrMigrateBootDrivePart(_In_ PBOOT_STORE_INI_CONTEXT BootStore)323 FreeLdrMigrateBootDrivePart(
324     _In_ PBOOT_STORE_INI_CONTEXT BootStore)
325 {
326     PINICACHEITERATOR Iterator;
327     PINI_SECTION OsIniSection;
328     PCWSTR SectionName, KeyData;
329 
330     /* Enumerate all the valid entries in the "Operating Systems" section */
331     Iterator = IniFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData);
332     if (!Iterator) return;
333     do
334     {
335         /* Search for an existing boot entry section */
336         OsIniSection = IniGetSection(BootStore->IniCache, SectionName);
337         if (!OsIniSection)
338             continue;
339 
340         /* Check for boot type to migrate */
341         if (!IniGetKey(OsIniSection, L"BootType", &KeyData) || !KeyData)
342         {
343             /* Certainly not a ReactOS installation */
344             DPRINT1("No BootType value present\n");
345             continue;
346         }
347         if ((_wcsicmp(KeyData, L"Drive")     == 0) ||
348             (_wcsicmp(KeyData, L"\"Drive\"") == 0) ||
349             (_wcsicmp(KeyData, L"Partition")     == 0) ||
350             (_wcsicmp(KeyData, L"\"Partition\"") == 0))
351         {
352             /* Modify the BootPath value */
353             IniAddKey(OsIniSection, L"BootType", L"BootSector");
354             goto migrate_drivepart;
355         }
356         if ((_wcsicmp(KeyData, L"BootSector")     == 0) ||
357             (_wcsicmp(KeyData, L"\"BootSector\"") == 0))
358         {
359 migrate_drivepart:
360             DPRINT("This is a '%S' boot entry\n", KeyData);
361             FreeLdrMigrateBootDrivePartWorker(OsIniSection);
362         }
363     }
364     while (IniFindNextValue(Iterator, &SectionName, &KeyData));
365 
366     IniFindClose(Iterator);
367 }
368 //////////////
369 
370 
371 static VOID
CreateCommonFreeLdrSections(IN OUT PBOOT_STORE_INI_CONTEXT BootStore)372 CreateCommonFreeLdrSections(
373     IN OUT PBOOT_STORE_INI_CONTEXT BootStore)
374 {
375     PINI_SECTION IniSection;
376 
377     /*
378      * Cache the "FREELOADER" section for our future usage.
379      */
380 
381     /* Create the "FREELOADER" section */
382     IniSection = IniAddSection(BootStore->IniCache, L"FREELOADER");
383     if (!IniSection)
384         DPRINT1("CreateCommonFreeLdrSections: Failed to create 'FREELOADER' section!\n");
385 
386     BootStore->OptionsIniSection = IniSection;
387 
388     /* TimeOut */
389     IniAddKey(BootStore->OptionsIniSection, L"TimeOut", L"0");
390 
391     /* Create "Display" section */
392     IniSection = IniAddSection(BootStore->IniCache, L"Display");
393 
394     /* TitleText and MinimalUI */
395     IniAddKey(IniSection, L"TitleText", L"ReactOS Boot Manager");
396     IniAddKey(IniSection, L"MinimalUI", L"Yes");
397 
398     /*
399      * Cache the "Operating Systems" section for our future usage.
400      */
401 
402     /* Create the "Operating Systems" section */
403     IniSection = IniAddSection(BootStore->IniCache, L"Operating Systems");
404     if (!IniSection)
405         DPRINT1("CreateCommonFreeLdrSections: Failed to create 'Operating Systems' section!\n");
406 
407     BootStore->OsIniSection = IniSection;
408 }
409 
410 static NTSTATUS
OpenIniBootLoaderStore(_Out_ PVOID * Handle,_In_ HANDLE PartitionDirectoryHandle,_In_ BOOT_STORE_TYPE Type,_In_ BOOT_STORE_OPENMODE OpenMode,_In_ BOOT_STORE_ACCESS Access)411 OpenIniBootLoaderStore(
412     _Out_ PVOID* Handle,
413     _In_ HANDLE PartitionDirectoryHandle, // _In_opt_
414     _In_ BOOT_STORE_TYPE Type,
415     _In_ BOOT_STORE_OPENMODE OpenMode,
416     _In_ BOOT_STORE_ACCESS Access)
417 {
418     NTSTATUS Status;
419     PBOOT_STORE_INI_CONTEXT BootStore;
420     UNICODE_STRING Name;
421     OBJECT_ATTRIBUTES ObjectAttributes;
422     IO_STATUS_BLOCK IoStatusBlock;
423     ACCESS_MASK DesiredAccess;
424     ULONG CreateDisposition;
425 
426     //
427     // WARNING! We support the INI creation *ONLY* for FreeLdr, and not for NTLDR
428     //
429     if ((Type == NtLdr) && (OpenMode == BS_CreateNew || OpenMode == BS_CreateAlways || OpenMode == BS_RecreateExisting))
430     {
431         DPRINT1("OpenIniBootLoaderStore() unsupported for NTLDR\n");
432         return STATUS_NOT_SUPPORTED;
433     }
434 
435     /* Create a boot store structure */
436     BootStore = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(*BootStore));
437     if (!BootStore)
438         return STATUS_INSUFFICIENT_RESOURCES;
439 
440     BootStore->Header.Type = Type;
441 
442     /*
443      * So far, we only use the INI cache. The file itself is not created or
444      * opened yet, therefore FileHandle, SectionHandle, ViewBase and FileSize
445      * are all NULL. We will use this fact to know that the INI file was indeed
446      * created, and not just opened as an existing file.
447      */
448     // BootStore->FileHandle = NULL;
449     BootStore->SectionHandle = NULL;
450     BootStore->ViewBase = NULL;
451     BootStore->FileSize = 0;
452 
453     /*
454      * Create or open the loader configuration INI file as necessary.
455      */
456     RtlInitUnicodeString(&Name, NtosBootLoaders[Type].LoaderConfigurationFile);
457     InitializeObjectAttributes(&ObjectAttributes,
458                                &Name,
459                                OBJ_CASE_INSENSITIVE,
460                                PartitionDirectoryHandle,
461                                NULL);
462 
463     DesiredAccess =
464         ((Access & BS_ReadAccess ) ? FILE_GENERIC_READ  : 0) |
465         ((Access & BS_WriteAccess) ? FILE_GENERIC_WRITE : 0);
466 
467     CreateDisposition = FILE_OPEN;
468     switch (OpenMode)
469     {
470         case BS_CreateNew:
471             CreateDisposition = FILE_CREATE;
472             break;
473         case BS_CheckExisting:
474         case BS_OpenExisting:
475             CreateDisposition = FILE_OPEN;
476             break;
477         case BS_OpenAlways:
478             CreateDisposition = FILE_OPEN_IF;
479             break;
480         case BS_RecreateExisting:
481             CreateDisposition = FILE_OVERWRITE;
482             break;
483         case BS_CreateAlways:
484             CreateDisposition = FILE_OVERWRITE_IF;
485             break;
486         default:
487             ASSERT(FALSE);
488     }
489 
490     IoStatusBlock.Information = 0;
491     Status = NtCreateFile(&BootStore->FileHandle,
492                           DesiredAccess | SYNCHRONIZE,
493                           &ObjectAttributes,
494                           &IoStatusBlock,
495                           NULL,
496                           FILE_ATTRIBUTE_NORMAL,
497                           FILE_SHARE_READ,
498                           CreateDisposition,
499                           FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE,
500                           NULL,
501                           0);
502 
503     if (OpenMode == BS_CheckExisting)
504     {
505         /* We just want to check for file existence. If we either succeeded
506          * opening the file, or we failed because it exists but we do not
507          * currently have access to it, return success in either case. */
508         BOOLEAN Success = (NT_SUCCESS(Status) || (Status == STATUS_ACCESS_DENIED));
509         if (!Success)
510         {
511             DPRINT1("Couldn't find Loader configuration file '%S'\n",
512                     NtosBootLoaders[Type].LoaderConfigurationFile);
513         }
514         if (BootStore->FileHandle)
515             NtClose(BootStore->FileHandle);
516         RtlFreeHeap(ProcessHeap, 0, BootStore);
517         return (Success ? STATUS_SUCCESS : Status);
518     }
519 
520     /*
521      * If create/open failed because the file is in read-only mode,
522      * change its attributes and re-attempt opening it.
523      */
524     if (Status == STATUS_ACCESS_DENIED) do
525     {
526         FILE_BASIC_INFORMATION FileInfo = {0};
527 
528         /* Reattempt to open it with limited access */
529         Status = NtCreateFile(&BootStore->FileHandle,
530                               FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
531                               &ObjectAttributes,
532                               &IoStatusBlock,
533                               NULL,
534                               FILE_ATTRIBUTE_NORMAL,
535                               FILE_SHARE_READ,
536                               FILE_OPEN,
537                               FILE_NO_INTERMEDIATE_BUFFERING |
538                               FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE,
539                               NULL,
540                               0);
541         /* Fail for real if we cannot open it that way */
542         if (!NT_SUCCESS(Status))
543             break;
544 
545         /* Reset attributes to normal, no read-only */
546         FileInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
547         /*
548          * We basically don't care about whether it succeeds:
549          * if it didn't, later open will fail.
550          */
551         NtSetInformationFile(BootStore->FileHandle, &IoStatusBlock,
552                              &FileInfo, sizeof(FileInfo),
553                              FileBasicInformation);
554 
555         /* Close file */
556         NtClose(BootStore->FileHandle);
557 
558         /* And re-attempt create/open */
559         Status = NtCreateFile(&BootStore->FileHandle,
560                               DesiredAccess | SYNCHRONIZE,
561                               &ObjectAttributes,
562                               &IoStatusBlock,
563                               NULL,
564                               FILE_ATTRIBUTE_NORMAL,
565                               FILE_SHARE_READ,
566                               CreateDisposition,
567                               FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE,
568                               NULL,
569                               0);
570     } while (0);
571     if (!NT_SUCCESS(Status))
572     {
573         DPRINT1("Couldn't open Loader configuration file '%S' (Status 0x%08lx)\n",
574                 NtosBootLoaders[Type].LoaderConfigurationFile, Status);
575         RtlFreeHeap(ProcessHeap, 0, BootStore);
576         return Status;
577     }
578 
579     BootStore->Header.ReadOnly = !(Access & BS_WriteAccess);
580 
581     if (IoStatusBlock.Information == FILE_CREATED     || // with: FILE_CREATE, FILE_OVERWRITE_IF, FILE_OPEN_IF, FILE_SUPERSEDE
582         IoStatusBlock.Information == FILE_OVERWRITTEN || // with: FILE_OVERWRITE, FILE_OVERWRITE_IF
583         IoStatusBlock.Information == FILE_SUPERSEDED)    // with: FILE_SUPERSEDE
584     {
585         /*
586          * The loader configuration INI file is (re)created
587          * fresh new, initialize its cache and its contents.
588          */
589         BootStore->IniCache = IniCacheCreate();
590         if (!BootStore->IniCache)
591         {
592             DPRINT1("IniCacheCreate() failed\n");
593             NtClose(BootStore->FileHandle);
594             RtlFreeHeap(ProcessHeap, 0, BootStore);
595             return STATUS_INSUFFICIENT_RESOURCES;
596         }
597 
598         if (Type == FreeLdr)
599             CreateCommonFreeLdrSections(BootStore);
600     }
601     else // if (IoStatusBlock.Information == FILE_OPENED) // with: FILE_OPEN, FILE_OPEN_IF
602     {
603         PINI_SECTION IniSection;
604 
605         /*
606          * The loader configuration INI file exists and is opened,
607          * map its file contents into memory.
608          */
609 #if 0
610         // FIXME: &BootStore->FileSize
611         Status = MapFile(BootStore->FileHandle,
612                          &BootStore->SectionHandle,
613                          &BootStore->ViewBase,
614                          (Access & BS_WriteAccess));
615         if (!NT_SUCCESS(Status))
616         {
617             DPRINT1("Failed to map Loader configuration file '%S' (Status 0x%08lx)\n",
618                     NtosBootLoaders[Type].LoaderConfigurationFile, Status);
619             NtClose(BootStore->FileHandle);
620             RtlFreeHeap(ProcessHeap, 0, BootStore);
621             return Status;
622         }
623 #else
624         BootStore->SectionHandle = UlongToPtr(1); // Workaround for CloseIniBootLoaderStore
625 #endif
626 
627         /* Open an *existing* INI configuration file */
628 #if 0
629         Status = IniCacheLoadFromMemory(&BootStore->IniCache,
630                                         BootStore->ViewBase,
631                                         BootStore->FileSize,
632                                         FALSE);
633 #else
634         Status = IniCacheLoadByHandle(&BootStore->IniCache, BootStore->FileHandle, FALSE);
635 #endif
636         if (!NT_SUCCESS(Status))
637         {
638             DPRINT1("IniCacheLoadFromMemory() failed (Status 0x%08lx)\n", Status);
639 #if 0
640             /* Finally, unmap and close the file */
641             UnMapAndCloseFile(BootStore->FileHandle,
642                               BootStore->SectionHandle,
643                               BootStore->ViewBase);
644 #else
645             NtClose(BootStore->FileHandle);
646 #endif
647             RtlFreeHeap(ProcessHeap, 0, BootStore);
648             return Status;
649         }
650 
651         if (Type == FreeLdr)
652         {
653             /*
654              * Cache the "FREELOADER" section for our future usage.
655              */
656 
657             /* Get or create the "FREELOADER" section */
658             IniSection = IniAddSection(BootStore->IniCache, L"FREELOADER");
659             if (!IniSection)
660                 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'FREELOADER' section!\n");
661 
662             BootStore->OptionsIniSection = IniSection;
663 
664             /*
665              * Cache the "Operating Systems" section for our future usage.
666              */
667 
668             /* Get or create the "Operating Systems" section */
669             IniSection = IniAddSection(BootStore->IniCache, L"Operating Systems");
670             if (!IniSection)
671                 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'Operating Systems' section!\n");
672 
673             BootStore->OsIniSection = IniSection;
674 
675             //
676             // TEMPORARY: Migrate the DEPRECATED BootDrive and BootPartition
677             // values of BootSector boot entries to the newer BootPath value.
678             //
679             FreeLdrMigrateBootDrivePart(BootStore);
680         }
681         else
682         if (Type == NtLdr)
683         {
684             /*
685              * Cache the "boot loader" section for our future usage.
686              */
687             /*
688              * HISTORICAL NOTE:
689              *
690              * While the "operating systems" section acquired its definitive
691              * name already when Windows NT was at its very early beta stage
692              * (NT 3.1 October 1991 Beta, 10-16-1991), this was not the case
693              * for its general settings section "boot loader".
694              *
695              * The following section names were successively introduced:
696              *
697              * - In NT 3.1 October 1991 Beta, 10-16-1991, using OS Loader V1.5,
698              *   the section was named "multiboot".
699              *
700              * - In the next public beta version NT 3.10.340 Beta, 10-12-1992,
701              *   using OS Loader V2.10, a new name was introduced: "flexboot".
702              *   This is around this time that the NT OS Loader was also
703              *   introduced as the "Windows NT FlexBoot" loader, as shown by
704              *   the Windows NT FAQs that circulated around this time:
705              *   http://cd.textfiles.com/cica9308/CIS_LIBS/WINNT/1/NTFAQ.TXT
706              *   http://cd.textfiles.com/cica/cica9308/UNZIPPED/NT/NTFAQ/FTP/NEWS/NTFAQ1.TXT
707              *   I can only hypothesize that the "FlexBoot" name was chosen
708              *   as a marketing coup, possibly to emphasise its "flexibility"
709              *   as a simple multiboot-aware boot manager.
710              *
711              * - A bit later, with NT 3.10.404 Beta, 3-7-1993, using an updated
712              *   version of OS Loader V2.10, the final section name "boot loader"
713              *   was introduced, and was kept since then.
714              *
715              * Due to the necessity to be able to boot and / or upgrade any
716              * Windows NT version at any time, including its NT Loader and the
717              * associated boot.ini file, all versions of NTLDR and the NT installer
718              * understand and parse these three section names, the default one
719              * being "boot loader", and if not present, they successively fall
720              * back to "flexboot" and then to "multiboot".
721              */
722 
723             /* Get the "boot loader" section */
724             IniSection = IniGetSection(BootStore->IniCache, L"boot loader");
725             if (!IniSection)
726             {
727                 /* Fall back to "flexboot" */
728                 IniSection = IniGetSection(BootStore->IniCache, L"flexboot");
729                 if (!IniSection)
730                 {
731                     /* Fall back to "multiboot" */
732                     IniSection = IniGetSection(BootStore->IniCache, L"multiboot");
733                 }
734             }
735 #if 0
736             if (!IniSection)
737             {
738                 /* It does not exist yet, so create it */
739                 IniSection = IniAddSection(BootStore->IniCache, L"boot loader");
740             }
741 #endif
742             if (!IniSection)
743                 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'boot loader' section!\n");
744 
745             BootStore->OptionsIniSection = IniSection;
746 
747             /*
748              * Cache the "Operating Systems" section for our future usage.
749              */
750 
751             /* Get or create the "Operating Systems" section */
752             IniSection = IniAddSection(BootStore->IniCache, L"operating systems");
753             if (!IniSection)
754                 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'operating systems' section!\n");
755 
756             BootStore->OsIniSection = IniSection;
757         }
758     }
759 
760     *Handle = BootStore;
761     return STATUS_SUCCESS;
762 }
763 
764 /**
765  * @brief
766  * Selectively changes the attributes of a file.
767  *
768  * @param[in]   FileHandle
769  * Handle to an opened file for which to change its attributes.
770  *
771  * @param[in]   MaskAttributes
772  * A mask specifying which attributes to change; any other attributes
773  * will be maintained as they are. If this parameter is zero, all of
774  * the attributes in *Attributes will be changed.
775  *
776  * @param[in,out]   Attributes
777  * In input, specifies the new attributes to set. Attributes that
778  * are not set, but are specified in MaskAttributes, are removed.
779  * In output, receives the original attributes of the file.
780  *
781  * @return
782  * STATUS_SUCCESS if the attributes were successfully changed,
783  * or a failure code if an error happened.
784  **/
785 static NTSTATUS
ProtectFile(_In_ HANDLE FileHandle,_In_ ULONG MaskAttributes,_Inout_ PULONG Attributes)786 ProtectFile(
787     _In_ HANDLE FileHandle,
788     _In_ ULONG MaskAttributes,
789     _Inout_ PULONG Attributes)
790 {
791     NTSTATUS Status;
792     IO_STATUS_BLOCK IoStatusBlock;
793     FILE_BASIC_INFORMATION FileInfo;
794     ULONG OldAttributes;
795 
796     /* Retrieve the original file attributes */
797     Status = NtQueryInformationFile(FileHandle,
798                                     &IoStatusBlock,
799                                     &FileInfo,
800                                     sizeof(FileInfo),
801                                     FileBasicInformation);
802     if (!NT_SUCCESS(Status))
803     {
804         DPRINT1("NtQueryInformationFile() failed (Status 0x%08lx)\n", Status);
805         return Status;
806     }
807     OldAttributes = FileInfo.FileAttributes;
808 
809     /* Modify the attributes and return the old ones */
810     if (MaskAttributes)
811         FileInfo.FileAttributes = (OldAttributes & ~MaskAttributes) | (*Attributes & MaskAttributes);
812     else
813         FileInfo.FileAttributes = *Attributes;
814 
815     *Attributes = OldAttributes;
816 
817     /* Set the new file attributes */
818     Status = NtSetInformationFile(FileHandle,
819                                   &IoStatusBlock,
820                                   &FileInfo,
821                                   sizeof(FileInfo),
822                                   FileBasicInformation);
823     if (!NT_SUCCESS(Status))
824         DPRINT1("NtSetInformationFile() failed (Status 0x%08lx)\n", Status);
825 
826     return Status;
827 }
828 
829 static NTSTATUS
CloseIniBootLoaderStore(_In_ PVOID Handle)830 CloseIniBootLoaderStore(
831     _In_ PVOID Handle)
832 {
833     /* Set or remove SYSTEM, HIDDEN and READONLY attributes */
834     static const ULONG ProtectAttribs =
835         (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
836 
837     PBOOT_STORE_INI_CONTEXT BootStore = (PBOOT_STORE_INI_CONTEXT)Handle;
838     NTSTATUS Status = STATUS_SUCCESS;
839     ULONG FileAttribs;
840 
841     ASSERT(BootStore);
842 
843     /* If the INI file was opened in read-only mode, skip saving */
844     if (BootStore->Header.ReadOnly)
845         goto Quit;
846 
847     /* If the INI file was already opened because it already existed, unprotect it */
848     if (BootStore->SectionHandle)
849     {
850         FileAttribs = 0;
851         Status = ProtectFile(BootStore->FileHandle, ProtectAttribs, &FileAttribs);
852         if (!NT_SUCCESS(Status))
853         {
854             DPRINT1("Could not unprotect INI boot store (Status 0x%08lx)\n", Status);
855             goto Quit;
856         }
857     }
858 
859     IniCacheSaveByHandle(BootStore->IniCache, BootStore->FileHandle);
860 
861     /* Re-protect the INI file */
862     FileAttribs = ProtectAttribs;
863     if (BootStore->Header.Type == FreeLdr)
864     {
865         // NOTE: CORE-19575: For the time being, don't add READONLY for ease
866         // of testing and modifying files, but it won't always stay this way.
867 	FileAttribs &= ~FILE_ATTRIBUTE_READONLY;
868     }
869     /*Status =*/ ProtectFile(BootStore->FileHandle, FileAttribs, &FileAttribs);
870     Status = STATUS_SUCCESS; // Ignore the status and just succeed.
871 
872 Quit:
873     IniCacheDestroy(BootStore->IniCache);
874 
875 #if 0
876     if (BootStore->SectionHandle)
877     {
878         /* Finally, unmap and close the file */
879         UnMapAndCloseFile(BootStore->FileHandle,
880                           BootStore->SectionHandle,
881                           BootStore->ViewBase);
882     }
883     else // if (BootStore->FileHandle)
884 #endif
885     {
886         /* Just close the file we have opened for creation */
887         NtClose(BootStore->FileHandle);
888     }
889 
890     /* Finally, free the boot store structure */
891     RtlFreeHeap(ProcessHeap, 0, BootStore);
892     return Status;
893 }
894 
895 
896 NTSTATUS
OpenBootStoreByHandle(_Out_ PVOID * Handle,_In_ HANDLE PartitionDirectoryHandle,_In_ BOOT_STORE_TYPE Type,_In_ BOOT_STORE_OPENMODE OpenMode,_In_ BOOT_STORE_ACCESS Access)897 OpenBootStoreByHandle(
898     _Out_ PVOID* Handle,
899     _In_ HANDLE PartitionDirectoryHandle, // _In_opt_
900     _In_ BOOT_STORE_TYPE Type,
901     _In_ BOOT_STORE_OPENMODE OpenMode,
902     _In_ BOOT_STORE_ACCESS Access)
903 {
904     /*
905      * NOTE: Currently we open & map the loader configuration file without
906      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
907      * and NTLDR's boot.ini files. But as soon as we'll implement support for
908      * BOOTMGR detection, the "configuration file" will be the BCD registry
909      * hive and then, we'll have instead to mount the hive & open it.
910      */
911 
912     if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax)
913     {
914         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type);
915         return STATUS_NOT_SUPPORTED;
916     }
917 
918     /*
919      * Verify the access modes to perform the open actions.
920      * The operating system may allow e.g. file creation even with
921      * read-only access, but we do not allow this because we want
922      * to protect any existing boot store file in case the caller
923      * specified such an open mode.
924      */
925     // if ((OpenMode == BS_CheckExisting) && !(Access & BS_ReadAccess))
926     //     return STATUS_ACCESS_DENIED;
927     if ((OpenMode == BS_CreateNew || OpenMode == BS_CreateAlways || OpenMode == BS_RecreateExisting) && !(Access & BS_WriteAccess))
928         return STATUS_ACCESS_DENIED;
929     if ((OpenMode == BS_OpenExisting || OpenMode == BS_OpenAlways) && !(Access & BS_ReadWriteAccess))
930         return STATUS_ACCESS_DENIED;
931 
932     return NtosBootLoaders[Type].OpenBootStore(Handle,
933                                                PartitionDirectoryHandle,
934                                                Type,
935                                                OpenMode,
936                                                Access);
937 }
938 
939 NTSTATUS
OpenBootStore_UStr(_Out_ PVOID * Handle,_In_ PUNICODE_STRING SystemPartitionPath,_In_ BOOT_STORE_TYPE Type,_In_ BOOT_STORE_OPENMODE OpenMode,_In_ BOOT_STORE_ACCESS Access)940 OpenBootStore_UStr(
941     _Out_ PVOID* Handle,
942     _In_ PUNICODE_STRING SystemPartitionPath,
943     _In_ BOOT_STORE_TYPE Type,
944     _In_ BOOT_STORE_OPENMODE OpenMode,
945     _In_ BOOT_STORE_ACCESS Access)
946 {
947     NTSTATUS Status;
948     OBJECT_ATTRIBUTES ObjectAttributes;
949     IO_STATUS_BLOCK IoStatusBlock;
950     HANDLE PartitionDirectoryHandle;
951 
952     /*
953      * NOTE: Currently we open & map the loader configuration file without
954      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
955      * and NTLDR's boot.ini files. But as soon as we'll implement support for
956      * BOOTMGR detection, the "configuration file" will be the BCD registry
957      * hive and then, we'll have instead to mount the hive & open it.
958      */
959 
960     if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax)
961     {
962         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type);
963         return STATUS_NOT_SUPPORTED;
964     }
965 
966     /* Open SystemPartition */
967     InitializeObjectAttributes(&ObjectAttributes,
968                                SystemPartitionPath,
969                                OBJ_CASE_INSENSITIVE,
970                                NULL,
971                                NULL);
972     Status = NtOpenFile(&PartitionDirectoryHandle,
973                         FILE_LIST_DIRECTORY | FILE_ADD_FILE /* | FILE_ADD_SUBDIRECTORY | FILE_TRAVERSE*/ | SYNCHRONIZE,
974                         &ObjectAttributes,
975                         &IoStatusBlock,
976                         FILE_SHARE_READ | FILE_SHARE_WRITE,
977                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE /* | FILE_OPEN_FOR_BACKUP_INTENT */);
978     if (!NT_SUCCESS(Status))
979     {
980         DPRINT1("Failed to open SystemPartition '%wZ' (Status 0x%08lx)\n",
981                 SystemPartitionPath, Status);
982         return Status;
983     }
984 
985     Status = OpenBootStoreByHandle(Handle,
986                                    PartitionDirectoryHandle,
987                                    Type,
988                                    OpenMode,
989                                    Access);
990 
991     /* Done! */
992     NtClose(PartitionDirectoryHandle);
993     return Status;
994 }
995 
996 NTSTATUS
OpenBootStore(_Out_ PVOID * Handle,_In_ PCWSTR SystemPartition,_In_ BOOT_STORE_TYPE Type,_In_ BOOT_STORE_OPENMODE OpenMode,_In_ BOOT_STORE_ACCESS Access)997 OpenBootStore(
998     _Out_ PVOID* Handle,
999     _In_ PCWSTR SystemPartition,
1000     _In_ BOOT_STORE_TYPE Type,
1001     _In_ BOOT_STORE_OPENMODE OpenMode,
1002     _In_ BOOT_STORE_ACCESS Access)
1003 {
1004     UNICODE_STRING SystemPartitionPath;
1005     RtlInitUnicodeString(&SystemPartitionPath, SystemPartition);
1006     return OpenBootStore_UStr(Handle,
1007                               &SystemPartitionPath,
1008                               Type,
1009                               OpenMode,
1010                               Access);
1011 }
1012 
1013 NTSTATUS
CloseBootStore(_In_ PVOID Handle)1014 CloseBootStore(
1015     _In_ PVOID Handle)
1016 {
1017     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1018 
1019     if (!BootStore)
1020         return STATUS_INVALID_PARAMETER;
1021 
1022     /*
1023      * NOTE: Currently we open & map the loader configuration file without
1024      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1025      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1026      * BOOTMGR detection, the "configuration file" will be the BCD registry
1027      * hive and then, we'll have instead to mount the hive & open it.
1028      */
1029 
1030     if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1031     {
1032         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type);
1033         return STATUS_NOT_SUPPORTED;
1034     }
1035 
1036     return NtosBootLoaders[BootStore->Type].CloseBootStore(Handle /* BootStore */);
1037 }
1038 
1039 
1040 static
1041 NTSTATUS
CreateNTOSEntry(IN PBOOT_STORE_INI_CONTEXT BootStore,IN ULONG_PTR BootEntryKey,IN PBOOT_STORE_ENTRY BootEntry)1042 CreateNTOSEntry(
1043     IN PBOOT_STORE_INI_CONTEXT BootStore,
1044     IN ULONG_PTR BootEntryKey,
1045     IN PBOOT_STORE_ENTRY BootEntry)
1046 {
1047     PINI_SECTION IniSection;
1048     PCWSTR Section = (PCWSTR)BootEntryKey;
1049 
1050     /* Insert the entry into the "Operating Systems" section */
1051     IniAddKey(BootStore->OsIniSection, Section, BootEntry->FriendlyName);
1052 
1053     /* Create a new section */
1054     IniSection = IniAddSection(BootStore->IniCache, Section);
1055 
1056     if (BootEntry->OsOptionsLength >= sizeof(NTOS_OPTIONS) &&
1057         RtlCompareMemory(&BootEntry->OsOptions /* Signature */,
1058                          NTOS_OPTIONS_SIGNATURE,
1059                          RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) ==
1060                          RTL_FIELD_SIZE(NTOS_OPTIONS, Signature))
1061     {
1062         PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
1063 
1064         /* BootType, SystemPath and Options */
1065         IniAddKey(IniSection, L"BootType", L"Windows2003");
1066         IniAddKey(IniSection, L"SystemPath", Options->OsLoadPath);
1067         IniAddKey(IniSection, L"Options", Options->OsLoadOptions);
1068     }
1069     else
1070     if (BootEntry->OsOptionsLength >= sizeof(BOOTSECTOR_OPTIONS) &&
1071         RtlCompareMemory(&BootEntry->OsOptions /* Signature */,
1072                          BOOTSECTOR_OPTIONS_SIGNATURE,
1073                          RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature)) ==
1074                          RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature))
1075     {
1076         PBOOTSECTOR_OPTIONS Options = (PBOOTSECTOR_OPTIONS)&BootEntry->OsOptions;
1077 
1078         /* BootType, BootPath and BootSector */
1079         IniAddKey(IniSection, L"BootType", L"BootSector");
1080         IniAddKey(IniSection, L"BootPath", Options->BootPath);
1081         IniAddKey(IniSection, L"BootSectorFile", Options->FileName);
1082     }
1083     else
1084     {
1085         // DPRINT1("Unsupported BootType %lu/'%*.s'\n",
1086                 // BootEntry->OsOptionsLength, 8, &BootEntry->OsOptions);
1087         DPRINT1("Unsupported BootType %lu\n", BootEntry->OsOptionsLength);
1088     }
1089 
1090     return STATUS_SUCCESS;
1091 }
1092 
1093 NTSTATUS
AddBootStoreEntry(IN PVOID Handle,IN PBOOT_STORE_ENTRY BootEntry,IN ULONG_PTR BootEntryKey)1094 AddBootStoreEntry(
1095     IN PVOID Handle,
1096     IN PBOOT_STORE_ENTRY BootEntry,
1097     IN ULONG_PTR BootEntryKey)
1098 {
1099     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1100 
1101     if (!BootStore || !BootEntry)
1102         return STATUS_INVALID_PARAMETER;
1103 
1104     /*
1105      * NOTE: Currently we open & map the loader configuration file without
1106      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1107      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1108      * BOOTMGR detection, the "configuration file" will be the BCD registry
1109      * hive and then, we'll have instead to mount the hive & open it.
1110      */
1111 
1112     //
1113     // FIXME!!
1114     //
1115 
1116     // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1117 
1118     if (BootStore->Type == FreeLdr)
1119     {
1120         if (BootEntry->Version != FreeLdr)
1121             return STATUS_INVALID_PARAMETER;
1122 
1123         return CreateNTOSEntry((PBOOT_STORE_INI_CONTEXT)BootStore,
1124                                BootEntryKey, BootEntry);
1125     }
1126     else
1127     if (BootStore->Type == NtLdr)
1128     {
1129         PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
1130         PWCHAR Buffer;
1131         ULONG BufferLength;
1132         PCWSTR InstallName, OsOptions;
1133         // ULONG InstallNameLength, OsOptionsLength;
1134         BOOLEAN IsNameNotQuoted;
1135 
1136         if (BootEntry->Version != NtLdr)
1137             return STATUS_INVALID_PARAMETER;
1138 
1139         if (BootEntry->OsOptionsLength < sizeof(NTOS_OPTIONS) ||
1140             RtlCompareMemory(&BootEntry->OsOptions /* Signature */,
1141                              NTOS_OPTIONS_SIGNATURE,
1142                              RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) !=
1143                              RTL_FIELD_SIZE(NTOS_OPTIONS, Signature))
1144         {
1145             // DPRINT1("Unsupported BootType '%S'\n", BootEntry->Version);
1146             DPRINT1("Unsupported BootType %lu\n", BootEntry->OsOptionsLength);
1147             return STATUS_SUCCESS; // STATUS_NOT_SUPPORTED;
1148         }
1149 
1150         InstallName = BootEntry->FriendlyName;
1151         OsOptions = Options->OsLoadOptions;
1152 
1153         // if (InstallNameLength == 0) InstallName = NULL;
1154         // if (OsOptionsLength == 0) OsOptions = NULL;
1155 
1156         IsNameNotQuoted = (InstallName[0] != L'\"' || InstallName[wcslen(InstallName)-1] != L'\"');
1157 
1158         BufferLength = (IsNameNotQuoted ? 2 /* Quotes for FriendlyName*/ : 0) + wcslen(InstallName);
1159         if (OsOptions)
1160             BufferLength += 1 /* Space between FriendlyName and options */ + wcslen(OsOptions);
1161         BufferLength++; /* NULL-termination */
1162 
1163         Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, BufferLength * sizeof(WCHAR));
1164         if (!Buffer)
1165             return STATUS_INSUFFICIENT_RESOURCES;
1166 
1167         *Buffer = UNICODE_NULL;
1168         if (IsNameNotQuoted) RtlStringCchCatW(Buffer, BufferLength, L"\"");
1169         RtlStringCchCatW(Buffer, BufferLength, InstallName);
1170         if (IsNameNotQuoted) RtlStringCchCatW(Buffer, BufferLength, L"\"");
1171         if (OsOptions)
1172         {
1173             RtlStringCchCatW(Buffer, BufferLength, L" ");
1174             RtlStringCchCatW(Buffer, BufferLength, OsOptions);
1175         }
1176 
1177         /* Insert the entry into the "Operating Systems" section */
1178         IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OsIniSection,
1179                   Options->OsLoadPath, Buffer);
1180 
1181         RtlFreeHeap(ProcessHeap, 0, Buffer);
1182         return STATUS_SUCCESS;
1183     }
1184     else
1185     {
1186         DPRINT1("Loader type %d is currently unsupported!\n", BootStore->Type);
1187         return STATUS_NOT_SUPPORTED;
1188     }
1189 }
1190 
1191 NTSTATUS
DeleteBootStoreEntry(IN PVOID Handle,IN ULONG_PTR BootEntryKey)1192 DeleteBootStoreEntry(
1193     IN PVOID Handle,
1194     IN ULONG_PTR BootEntryKey)
1195 {
1196     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1197 
1198     if (!BootStore)
1199         return STATUS_INVALID_PARAMETER;
1200 
1201     /*
1202      * NOTE: Currently we open & map the loader configuration file without
1203      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1204      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1205      * BOOTMGR detection, the "configuration file" will be the BCD registry
1206      * hive and then, we'll have instead to mount the hive & open it.
1207      */
1208 
1209     //
1210     // FIXME!!
1211     //
1212 
1213     // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1214     if (BootStore->Type != FreeLdr)
1215     {
1216         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type);
1217         return STATUS_NOT_SUPPORTED;
1218     }
1219 
1220     // FIXME! This function needs my INI library rewrite to be implemented!!
1221     UNIMPLEMENTED;
1222     return STATUS_NOT_IMPLEMENTED;
1223 }
1224 
1225 NTSTATUS
ModifyBootStoreEntry(IN PVOID Handle,IN PBOOT_STORE_ENTRY BootEntry)1226 ModifyBootStoreEntry(
1227     IN PVOID Handle,
1228     IN PBOOT_STORE_ENTRY BootEntry)
1229 {
1230     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1231 
1232     if (!BootStore || !BootEntry)
1233         return STATUS_INVALID_PARAMETER;
1234 
1235     /*
1236      * NOTE: Currently we open & map the loader configuration file without
1237      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1238      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1239      * BOOTMGR detection, the "configuration file" will be the BCD registry
1240      * hive and then, we'll have instead to mount the hive & open it.
1241      */
1242 
1243     //
1244     // FIXME!!
1245     //
1246 
1247     // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1248     if (BootStore->Type != FreeLdr)
1249     {
1250         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type);
1251         return STATUS_NOT_SUPPORTED;
1252     }
1253 
1254     // FIXME! This function needs my INI library rewrite to operate properly!!
1255     UNIMPLEMENTED;
1256     return STATUS_NOT_IMPLEMENTED;
1257 }
1258 
1259 NTSTATUS
QueryBootStoreEntry(IN PVOID Handle,IN ULONG_PTR BootEntryKey,OUT PBOOT_STORE_ENTRY BootEntry)1260 QueryBootStoreEntry(
1261     IN PVOID Handle,
1262     IN ULONG_PTR BootEntryKey,
1263     OUT PBOOT_STORE_ENTRY BootEntry) // Technically this should be PBOOT_STORE_ENTRY*
1264 {
1265     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1266 
1267     if (!BootStore)
1268         return STATUS_INVALID_PARAMETER;
1269 
1270     /*
1271      * NOTE: Currently we open & map the loader configuration file without
1272      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1273      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1274      * BOOTMGR detection, the "configuration file" will be the BCD registry
1275      * hive and then, we'll have instead to mount the hive & open it.
1276      */
1277 
1278     //
1279     // FIXME!!
1280     //
1281 
1282     // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1283     if (BootStore->Type != FreeLdr)
1284     {
1285         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type);
1286         return STATUS_NOT_SUPPORTED;
1287     }
1288 
1289     // FIXME! This function needs my INI library rewrite to be implemented!!
1290     UNIMPLEMENTED;
1291     return STATUS_NOT_IMPLEMENTED;
1292 }
1293 
1294 NTSTATUS
QueryBootStoreOptions(IN PVOID Handle,IN OUT PBOOT_STORE_OPTIONS BootOptions)1295 QueryBootStoreOptions(
1296     IN PVOID Handle,
1297     IN OUT PBOOT_STORE_OPTIONS BootOptions
1298 /* , IN PULONG BootOptionsLength */ )
1299 {
1300     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1301     PCWSTR TimeoutStr;
1302 
1303     if (!BootStore || !BootOptions)
1304         return STATUS_INVALID_PARAMETER;
1305 
1306     /*
1307      * NOTE: Currently we open & map the loader configuration file without
1308      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1309      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1310      * BOOTMGR detection, the "configuration file" will be the BCD registry
1311      * hive and then, we'll have instead to mount the hive & open it.
1312      */
1313 
1314     //
1315     // FIXME!!
1316     //
1317 
1318     // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1319     if (BootStore->Type != FreeLdr && BootStore->Type != NtLdr)
1320     {
1321         DPRINT1("Loader type %d is currently unsupported!\n", BootStore->Type);
1322         return STATUS_NOT_SUPPORTED;
1323     }
1324 
1325     BootOptions->Timeout = 0;
1326     BootOptions->CurrentBootEntryKey = 0;
1327     BootOptions->NextBootEntryKey = 0;
1328 
1329     if (IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
1330                   BootOptionNames[BootStore->Type][BO_TimeOut],
1331                   &TimeoutStr) && TimeoutStr)
1332     {
1333         BootOptions->Timeout = _wtoi(TimeoutStr);
1334     }
1335 
1336     IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
1337               BootOptionNames[BootStore->Type][BO_DefaultOS],
1338               (PCWSTR*)&BootOptions->NextBootEntryKey);
1339 
1340     /*
1341      * NOTE: BootOptions->CurrentBootEntryKey is an informative field only.
1342      * It indicates which boot entry has been selected for starting the
1343      * current OS instance. Such information is NOT stored in the INI file,
1344      * but has to be determined via other means. On UEFI the 'BootCurrent'
1345      * environment variable does that. Otherwise, one could heuristically
1346      * determine it by comparing the boot path and options of each entry
1347      * with those used by the current OS instance.
1348      * Since we currently do not need this information (and it can be costly
1349      * to determine), BootOptions->CurrentBootEntryKey is not evaluated.
1350      */
1351 
1352     return STATUS_SUCCESS;
1353 }
1354 
1355 NTSTATUS
SetBootStoreOptions(IN PVOID Handle,IN PBOOT_STORE_OPTIONS BootOptions,IN ULONG FieldsToChange)1356 SetBootStoreOptions(
1357     IN PVOID Handle,
1358     IN PBOOT_STORE_OPTIONS BootOptions,
1359     IN ULONG FieldsToChange)
1360 {
1361     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1362 
1363     if (!BootStore || !BootOptions)
1364         return STATUS_INVALID_PARAMETER;
1365 
1366     /*
1367      * NOTE: Currently we open & map the loader configuration file without
1368      * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini
1369      * and NTLDR's boot.ini files. But as soon as we'll implement support for
1370      * BOOTMGR detection, the "configuration file" will be the BCD registry
1371      * hive and then, we'll have instead to mount the hive & open it.
1372      */
1373 
1374     //
1375     // FIXME!!
1376     //
1377 
1378     // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1379     if (BootStore->Type != FreeLdr && BootStore->Type != NtLdr)
1380     {
1381         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type);
1382         return STATUS_NOT_SUPPORTED;
1383     }
1384 
1385     // if (BootOptions->Length < sizeof(*BootOptions))
1386     //     return STATUS_INVALID_PARAMETER;
1387 
1388     if (FieldsToChange & BOOT_OPTIONS_TIMEOUT)
1389     {
1390         WCHAR TimeoutStr[15];
1391         RtlStringCchPrintfW(TimeoutStr, ARRAYSIZE(TimeoutStr), L"%d", BootOptions->Timeout);
1392         IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
1393                   BootOptionNames[BootStore->Type][BO_TimeOut],
1394                   TimeoutStr);
1395     }
1396     if (FieldsToChange & BOOT_OPTIONS_NEXT_BOOTENTRY_KEY)
1397     {
1398         IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
1399                   BootOptionNames[BootStore->Type][BO_DefaultOS],
1400                   (PCWSTR)BootOptions->NextBootEntryKey);
1401     }
1402 
1403     return STATUS_SUCCESS;
1404 }
1405 
1406 
1407 static NTSTATUS
FreeLdrEnumerateBootEntries(IN PBOOT_STORE_INI_CONTEXT BootStore,IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,IN PVOID Parameter OPTIONAL)1408 FreeLdrEnumerateBootEntries(
1409     IN PBOOT_STORE_INI_CONTEXT BootStore,
1410 //  IN ULONG Flags, // Determine which data to retrieve
1411     IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,
1412     IN PVOID Parameter OPTIONAL)
1413 {
1414     NTSTATUS Status = STATUS_SUCCESS;
1415     PINICACHEITERATOR Iterator;
1416     PINI_SECTION OsIniSection;
1417     PCWSTR SectionName, KeyData;
1418     UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) +
1419                       max(sizeof(NTOS_OPTIONS), sizeof(BOOTSECTOR_OPTIONS))];
1420     PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
1421     PWCHAR Buffer;
1422 
1423     /* Enumerate all the valid installations listed in the "Operating Systems" section */
1424     Iterator = IniFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData);
1425     if (!Iterator) return STATUS_SUCCESS;
1426     do
1427     {
1428         PCWSTR InstallName;
1429         ULONG InstallNameLength;
1430 
1431         /* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */
1432         if (*KeyData == L'"')
1433         {
1434             /* Quoted name, copy up to the closing quote */
1435             PWCHAR End = wcschr(KeyData + 1, L'"');
1436 
1437             if (End)
1438             {
1439                 /* Skip the first quote */
1440                 InstallName = KeyData + 1;
1441                 InstallNameLength = End - InstallName;
1442             }
1443             else // if (!End)
1444             {
1445                 /* No corresponding closing quote, so we include the first one in the InstallName */
1446                 InstallName = KeyData;
1447                 InstallNameLength = wcslen(InstallName);
1448             }
1449             if (InstallNameLength == 0) InstallName = NULL;
1450         }
1451         else
1452         {
1453             /* Non-quoted name, copy everything */
1454             InstallName = KeyData;
1455             InstallNameLength = wcslen(InstallName);
1456             if (InstallNameLength == 0) InstallName = NULL;
1457         }
1458 
1459         /* Allocate the temporary buffer */
1460         Buffer = NULL;
1461         if (InstallNameLength)
1462             Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, (InstallNameLength + 1) * sizeof(WCHAR));
1463         if (Buffer)
1464         {
1465             RtlCopyMemory(Buffer, InstallName, InstallNameLength * sizeof(WCHAR));
1466             Buffer[InstallNameLength] = UNICODE_NULL;
1467             InstallName = Buffer;
1468         }
1469 
1470         DPRINT("Boot entry '%S' in OS section '%S'\n", InstallName, SectionName);
1471 
1472         BootEntry->Version = FreeLdr;
1473         BootEntry->BootEntryKey = MAKESTRKEY(SectionName);
1474         BootEntry->FriendlyName = InstallName;
1475         BootEntry->BootFilePath = NULL;
1476         BootEntry->OsOptionsLength = 0;
1477 
1478         /* Search for an existing boot entry section */
1479         OsIniSection = IniGetSection(BootStore->IniCache, SectionName);
1480         if (!OsIniSection)
1481             goto DoEnum;
1482 
1483         /* Check for supported boot type */
1484         if (!IniGetKey(OsIniSection, L"BootType", &KeyData) || !KeyData)
1485         {
1486             /* Certainly not a ReactOS installation */
1487             DPRINT1("No BootType value present\n");
1488             goto DoEnum;
1489         }
1490 
1491         // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ?
1492         if ((_wcsicmp(KeyData, L"Windows2003")     == 0) ||
1493             (_wcsicmp(KeyData, L"\"Windows2003\"") == 0))
1494         {
1495             /* BootType is Windows2003 */
1496             PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
1497 
1498             DPRINT("This is a '%S' boot entry\n", KeyData);
1499 
1500             BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS);
1501             RtlCopyMemory(Options->Signature,
1502                           NTOS_OPTIONS_SIGNATURE,
1503                           RTL_FIELD_SIZE(NTOS_OPTIONS, Signature));
1504 
1505             // BootEntry->BootFilePath = NULL;
1506 
1507             /* Check its SystemPath */
1508             Options->OsLoadPath = NULL;
1509             if (IniGetKey(OsIniSection, L"SystemPath", &KeyData))
1510                 Options->OsLoadPath = KeyData;
1511             // KeyData == SystemRoot;
1512 
1513             /* Check the optional Options */
1514             Options->OsLoadOptions = NULL;
1515             if (IniGetKey(OsIniSection, L"Options", &KeyData))
1516                 Options->OsLoadOptions = KeyData;
1517         }
1518         else
1519         if ((_wcsicmp(KeyData, L"BootSector")     == 0) ||
1520             (_wcsicmp(KeyData, L"\"BootSector\"") == 0))
1521         {
1522             /* BootType is BootSector */
1523             PBOOTSECTOR_OPTIONS Options = (PBOOTSECTOR_OPTIONS)&BootEntry->OsOptions;
1524 
1525             DPRINT("This is a '%S' boot entry\n", KeyData);
1526 
1527             BootEntry->OsOptionsLength = sizeof(BOOTSECTOR_OPTIONS);
1528             RtlCopyMemory(Options->Signature,
1529                           BOOTSECTOR_OPTIONS_SIGNATURE,
1530                           RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature));
1531 
1532             // BootEntry->BootFilePath = NULL;
1533 
1534             /* Check its BootPath */
1535             Options->BootPath = NULL;
1536             if (IniGetKey(OsIniSection, L"BootPath", &KeyData))
1537                 Options->BootPath = KeyData;
1538 
1539             /* Check its BootSector */
1540             Options->FileName = NULL;
1541             if (IniGetKey(OsIniSection, L"BootSectorFile", &KeyData))
1542                 Options->FileName = KeyData;
1543         }
1544         else
1545         {
1546             DPRINT1("Unrecognized BootType value '%S'\n", KeyData);
1547             // goto DoEnum;
1548         }
1549 
1550 DoEnum:
1551         /* Call the user enumeration routine callback */
1552         Status = EnumBootEntriesRoutine(FreeLdr, BootEntry, Parameter);
1553 
1554         /* Free temporary buffers */
1555         if (Buffer)
1556             RtlFreeHeap(ProcessHeap, 0, Buffer);
1557 
1558         /* Stop the enumeration if needed */
1559         if (!NT_SUCCESS(Status))
1560             break;
1561     }
1562     while (IniFindNextValue(Iterator, &SectionName, &KeyData));
1563 
1564     IniFindClose(Iterator);
1565     return Status;
1566 }
1567 
1568 static NTSTATUS
NtLdrEnumerateBootEntries(IN PBOOT_STORE_INI_CONTEXT BootStore,IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,IN PVOID Parameter OPTIONAL)1569 NtLdrEnumerateBootEntries(
1570     IN PBOOT_STORE_INI_CONTEXT BootStore,
1571 //  IN ULONG Flags, // Determine which data to retrieve
1572     IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,
1573     IN PVOID Parameter OPTIONAL)
1574 {
1575     NTSTATUS Status = STATUS_SUCCESS;
1576     PINICACHEITERATOR Iterator;
1577     PCWSTR SectionName, KeyData;
1578     UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)];
1579     PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
1580     PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
1581     PWCHAR Buffer;
1582     ULONG BufferLength;
1583 
1584     /* Enumerate all the valid installations */
1585     Iterator = IniFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData);
1586     if (!Iterator) return STATUS_SUCCESS;
1587     do
1588     {
1589         PCWSTR InstallName, OsOptions;
1590         ULONG InstallNameLength, OsOptionsLength;
1591 
1592         /* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */
1593         if (*KeyData == L'"')
1594         {
1595             /* Quoted name, copy up to the closing quote */
1596             OsOptions = wcschr(KeyData + 1, L'"');
1597 
1598             /* Retrieve the starting point of the installation name and the OS options */
1599             if (OsOptions)
1600             {
1601                 /* Skip the first quote */
1602                 InstallName = KeyData + 1;
1603                 InstallNameLength = OsOptions - InstallName;
1604                 if (InstallNameLength == 0) InstallName = NULL;
1605 
1606                 /* Skip the ending quote (if found) */
1607                 ++OsOptions;
1608 
1609                 /* Skip any whitespace */
1610                 while (iswspace(*OsOptions)) ++OsOptions;
1611                 /* Get its final length */
1612                 OsOptionsLength = wcslen(OsOptions);
1613                 if (OsOptionsLength == 0) OsOptions = NULL;
1614             }
1615             else
1616             {
1617                 /* No corresponding closing quote, so we include the first one in the InstallName */
1618                 InstallName = KeyData;
1619                 InstallNameLength = wcslen(InstallName);
1620                 if (InstallNameLength == 0) InstallName = NULL;
1621 
1622                 /* There are no OS options */
1623                 // OsOptions = NULL;
1624                 OsOptionsLength = 0;
1625             }
1626         }
1627         else
1628         {
1629             /* Non-quoted name, copy everything */
1630 
1631             /* Retrieve the starting point of the installation name */
1632             InstallName = KeyData;
1633             InstallNameLength = wcslen(InstallName);
1634             if (InstallNameLength == 0) InstallName = NULL;
1635 
1636             /* There are no OS options */
1637             OsOptions = NULL;
1638             OsOptionsLength = 0;
1639         }
1640 
1641         /* Allocate the temporary buffer */
1642         Buffer = NULL;
1643         BufferLength = (InstallNameLength + OsOptionsLength) * sizeof(WCHAR);
1644         if (BufferLength)
1645             Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, BufferLength + 2*sizeof(UNICODE_NULL));
1646         if (Buffer)
1647         {
1648             PWCHAR ptr;
1649 
1650             /* Copy the installation name, and make InstallName point into the buffer */
1651             if (InstallName && InstallNameLength)
1652             {
1653                 ptr = Buffer;
1654                 RtlCopyMemory(ptr, InstallName, InstallNameLength * sizeof(WCHAR));
1655                 ptr[InstallNameLength] = UNICODE_NULL;
1656                 InstallName = ptr;
1657             }
1658 
1659             /* Copy the OS options, and make OsOptions point into the buffer */
1660             if (OsOptions && OsOptionsLength)
1661             {
1662                 ptr = Buffer + InstallNameLength + 1;
1663                 RtlCopyMemory(ptr, OsOptions, OsOptionsLength * sizeof(WCHAR));
1664                 ptr[OsOptionsLength] = UNICODE_NULL;
1665                 OsOptions = ptr;
1666             }
1667         }
1668 
1669         DPRINT1("Boot entry '%S' in OS section (path) '%S'\n", InstallName, SectionName);
1670         // SectionName == SystemRoot;
1671 
1672         BootEntry->Version = NtLdr;
1673         BootEntry->BootEntryKey = 0; // FIXME??
1674         BootEntry->FriendlyName = InstallName;
1675         BootEntry->BootFilePath = NULL;
1676 
1677         BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS);
1678         RtlCopyMemory(Options->Signature,
1679                       NTOS_OPTIONS_SIGNATURE,
1680                       RTL_FIELD_SIZE(NTOS_OPTIONS, Signature));
1681 
1682         Options->OsLoadPath    = SectionName;
1683         Options->OsLoadOptions = OsOptions;
1684 
1685         /* Call the user enumeration routine callback */
1686         Status = EnumBootEntriesRoutine(NtLdr, BootEntry, Parameter);
1687 
1688         /* Free temporary buffers */
1689         if (Buffer)
1690             RtlFreeHeap(ProcessHeap, 0, Buffer);
1691 
1692         /* Stop the enumeration if needed */
1693         if (!NT_SUCCESS(Status))
1694             break;
1695     }
1696     while (IniFindNextValue(Iterator, &SectionName, &KeyData));
1697 
1698     IniFindClose(Iterator);
1699     return Status;
1700 }
1701 
1702 NTSTATUS
EnumerateBootStoreEntries(IN PVOID Handle,IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,IN PVOID Parameter OPTIONAL)1703 EnumerateBootStoreEntries(
1704     IN PVOID Handle,
1705 //  IN ULONG Flags, // Determine which data to retrieve
1706     IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine,
1707     IN PVOID Parameter OPTIONAL)
1708 {
1709     PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
1710 
1711     if (!BootStore)
1712         return STATUS_INVALID_PARAMETER;
1713 
1714     if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax)
1715     {
1716         DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type);
1717         /**/return STATUS_SUCCESS;/**/
1718         // return STATUS_INVALID_PARAMETER;
1719     }
1720 
1721     return NtosBootLoaders[BootStore->Type].EnumBootStoreEntries(
1722                 (PBOOT_STORE_INI_CONTEXT)BootStore, // Flags,
1723                 EnumBootEntriesRoutine, Parameter);
1724 }
1725 
1726 /* EOF */
1727