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