xref: /reactos/base/setup/lib/registry.c (revision 7eead935)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS Setup Library
22  * FILE:            base/setup/lib/registry.c
23  * PURPOSE:         Registry creation functions
24  * PROGRAMMERS:     ...
25  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include "precomp.h"
31 #include "filesup.h"
32 #include "infsupp.h"
33 #include "regutil.h"
34 
35 #include "registry.h"
36 
37 #define NDEBUG
38 #include <debug.h>
39 
40 
41 // #ifdef __REACTOS__
42 #if 1 // FIXME: Disable if setupapi.h is included in the code...
43 #define FLG_ADDREG_BINVALUETYPE           0x00000001
44 #define FLG_ADDREG_NOCLOBBER              0x00000002
45 #define FLG_ADDREG_DELVAL                 0x00000004
46 #define FLG_ADDREG_APPEND                 0x00000008
47 #define FLG_ADDREG_KEYONLY                0x00000010
48 #define FLG_ADDREG_OVERWRITEONLY          0x00000020
49 #define FLG_ADDREG_TYPE_SZ                0x00000000
50 #define FLG_ADDREG_TYPE_MULTI_SZ          0x00010000
51 #define FLG_ADDREG_TYPE_EXPAND_SZ         0x00020000
52 #define FLG_ADDREG_TYPE_BINARY           (0x00000000 | FLG_ADDREG_BINVALUETYPE)
53 #define FLG_ADDREG_TYPE_DWORD            (0x00010000 | FLG_ADDREG_BINVALUETYPE)
54 #define FLG_ADDREG_TYPE_NONE             (0x00020000 | FLG_ADDREG_BINVALUETYPE)
55 #define FLG_ADDREG_TYPE_MASK             (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
56 #endif
57 
58 /* GLOBALS ******************************************************************/
59 
60 #define REGISTRY_SETUP_MACHINE  L"\\Registry\\Machine\\SYSTEM\\USetup_Machine\\"
61 #define REGISTRY_SETUP_USER     L"\\Registry\\Machine\\SYSTEM\\USetup_User\\"
62 
63 typedef struct _ROOT_KEY
64 {
65     PCWSTR Name;
66     PCWSTR MountPoint;
67     HANDLE Handle;
68 } ROOT_KEY, *PROOT_KEY;
69 
70 ROOT_KEY RootKeys[] =
71 {
72     { L"HKCR", REGISTRY_SETUP_MACHINE L"SOFTWARE\\Classes\\", NULL },   /* "\\Registry\\Machine\\SOFTWARE\\Classes\\" */ // HKEY_CLASSES_ROOT
73     { L"HKCU", REGISTRY_SETUP_USER    L".DEFAULT\\"         , NULL },   /* "\\Registry\\User\\.DEFAULT\\" */             // HKEY_CURRENT_USER
74     { L"HKLM", REGISTRY_SETUP_MACHINE                       , NULL },   /* "\\Registry\\Machine\\"        */             // HKEY_LOCAL_MACHINE
75     { L"HKU" , REGISTRY_SETUP_USER                          , NULL },   /* "\\Registry\\User\\"           */             // HKEY_USERS
76 #if 0
77     { L"HKR", NULL, NULL },
78 #endif
79 };
80 
81 /* FUNCTIONS ****************************************************************/
82 
83 #define IsPredefKey(HKey)       \
84     (((ULONG_PTR)(HKey) & 0xF0000000) == 0x80000000)
85 
86 #define GetPredefKeyIndex(HKey) \
87     ((ULONG_PTR)(HKey) & 0x0FFFFFFF)
88 
89 HANDLE
90 GetRootKeyByPredefKey(
91     IN HANDLE KeyHandle,
92     OUT PCWSTR* RootKeyMountPoint OPTIONAL)
93 {
94     ULONG_PTR Index = GetPredefKeyIndex(KeyHandle);
95 
96     if (!IsPredefKey(KeyHandle))
97         return NULL;
98     if (Index >= ARRAYSIZE(RootKeys))
99         return NULL;
100 
101     if (RootKeyMountPoint)
102         *RootKeyMountPoint = RootKeys[Index].MountPoint;
103     return RootKeys[Index].Handle;
104 }
105 
106 HANDLE
107 GetRootKeyByName(
108     IN PCWSTR RootKeyName,
109     OUT PCWSTR* RootKeyMountPoint OPTIONAL)
110 {
111     UCHAR i;
112 
113     for (i = 0; i < ARRAYSIZE(RootKeys); ++i)
114     {
115         if (!_wcsicmp(RootKeyName, RootKeys[i].Name))
116         {
117             if (RootKeyMountPoint)
118                 *RootKeyMountPoint = RootKeys[i].MountPoint;
119             return RootKeys[i].Handle;
120         }
121     }
122 
123     return NULL;
124 }
125 
126 
127 /***********************************************************************
128  *            append_multi_sz_value
129  *
130  * Append a multisz string to a multisz registry value.
131  */
132 // NOTE: Synced with setupapi/install.c ; see also mkhive/reginf.c
133 #if 0
134 static void
135 append_multi_sz_value (HANDLE hkey,
136                        const WCHAR *value,
137                        const WCHAR *strings,
138                        DWORD str_size )
139 {
140     DWORD size, type, total;
141     WCHAR *buffer, *p;
142 
143     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
144     if (type != REG_MULTI_SZ) return;
145 
146     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + str_size * sizeof(WCHAR) ))) return;
147     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
148 
149     /* compare each string against all the existing ones */
150     total = size;
151     while (*strings)
152     {
153         int len = strlenW(strings) + 1;
154 
155         for (p = buffer; *p; p += strlenW(p) + 1)
156             if (!strcmpiW( p, strings )) break;
157 
158         if (!*p)  /* not found, need to append it */
159         {
160             memcpy( p, strings, len * sizeof(WCHAR) );
161             p[len] = 0;
162             total += len;
163         }
164         strings += len;
165     }
166     if (total != size)
167     {
168         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
169         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
170     }
171  done:
172     HeapFree( GetProcessHeap(), 0, buffer );
173 }
174 #endif
175 
176 /***********************************************************************
177  *            delete_multi_sz_value
178  *
179  * Remove a string from a multisz registry value.
180  */
181 #if 0
182 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
183 {
184     DWORD size, type;
185     WCHAR *buffer, *src, *dst;
186 
187     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
188     if (type != REG_MULTI_SZ) return;
189     /* allocate double the size, one for value before and one for after */
190     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
191     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
192     src = buffer;
193     dst = buffer + size;
194     while (*src)
195     {
196         int len = strlenW(src) + 1;
197         if (strcmpiW( src, string ))
198         {
199             memcpy( dst, src, len * sizeof(WCHAR) );
200             dst += len;
201         }
202         src += len;
203     }
204     *dst++ = 0;
205     if (dst != buffer + 2*size)  /* did we remove something? */
206     {
207         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
208         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
209                         (BYTE *)(buffer + size), dst - (buffer + size) );
210     }
211  done:
212     HeapFree( GetProcessHeap(), 0, buffer );
213 }
214 #endif
215 
216 /***********************************************************************
217  *            do_reg_operation
218  *
219  * Perform an add/delete registry operation depending on the flags.
220  */
221 static BOOLEAN
222 do_reg_operation(HANDLE KeyHandle,
223                  PUNICODE_STRING ValueName,
224                  PINFCONTEXT Context,
225                  ULONG Flags)
226 {
227   WCHAR EmptyStr = 0;
228   ULONG Type;
229   ULONG Size;
230 
231   if (Flags & FLG_ADDREG_DELVAL)  /* deletion */
232     {
233 #if 0
234       if (ValueName)
235         {
236           RegDeleteValueW( KeyHandle, ValueName );
237         }
238       else
239         {
240           RegDeleteKeyW( KeyHandle, NULL );
241         }
242 #endif
243       return TRUE;
244     }
245 
246   if (Flags & FLG_ADDREG_KEYONLY)
247     return TRUE;
248 
249 #if 0
250   if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
251     {
252       BOOL exists = !RegQueryValueExW( hkey, ValueName, NULL, NULL, NULL, NULL );
253       if (exists && (flags & FLG_ADDREG_NOCLOBBER))
254         return TRUE;
255       if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY))
256         return TRUE;
257     }
258 #endif
259 
260   switch (Flags & FLG_ADDREG_TYPE_MASK)
261     {
262       case FLG_ADDREG_TYPE_SZ:
263         Type = REG_SZ;
264         break;
265 
266       case FLG_ADDREG_TYPE_MULTI_SZ:
267         Type = REG_MULTI_SZ;
268         break;
269 
270       case FLG_ADDREG_TYPE_EXPAND_SZ:
271         Type = REG_EXPAND_SZ;
272         break;
273 
274       case FLG_ADDREG_TYPE_BINARY:
275         Type = REG_BINARY;
276         break;
277 
278       case FLG_ADDREG_TYPE_DWORD:
279         Type = REG_DWORD;
280         break;
281 
282       case FLG_ADDREG_TYPE_NONE:
283         Type = REG_NONE;
284         break;
285 
286       default:
287         Type = Flags >> 16;
288         break;
289     }
290 
291   if (!(Flags & FLG_ADDREG_BINVALUETYPE) ||
292       (Type == REG_DWORD && SpInfGetFieldCount(Context) == 5))
293     {
294       PWCHAR Str = NULL;
295 
296       if (Type == REG_MULTI_SZ)
297         {
298           if (!SpInfGetMultiSzField(Context, 5, NULL, 0, &Size))
299             Size = 0;
300 
301           if (Size)
302             {
303               Str = (WCHAR*) RtlAllocateHeap(ProcessHeap, 0, Size * sizeof(WCHAR));
304               if (Str == NULL)
305                 return FALSE;
306 
307               SpInfGetMultiSzField(Context, 5, Str, Size, NULL);
308             }
309 
310           if (Flags & FLG_ADDREG_APPEND)
311             {
312               if (Str == NULL)
313                 return TRUE;
314 
315               DPRINT1("append_multi_sz_value '%S' commented out, WHY??\n", ValueName);
316 //            append_multi_sz_value( hkey, value, str, size );
317 
318               RtlFreeHeap (ProcessHeap, 0, Str);
319               return TRUE;
320             }
321           /* else fall through to normal string handling */
322         }
323       else
324         {
325           if (!SpInfGetStringField(Context, 5, NULL, 0, &Size))
326             Size = 0;
327 
328           if (Size)
329             {
330               Str = (WCHAR*)RtlAllocateHeap(ProcessHeap, 0, Size * sizeof(WCHAR));
331               if (Str == NULL)
332                 return FALSE;
333 
334               SpInfGetStringField(Context, 5, Str, Size, NULL);
335             }
336         }
337 
338       if (Type == REG_DWORD)
339         {
340           ULONG dw = Str ? wcstoul (Str, NULL, 0) : 0;
341 
342           DPRINT("setting dword %wZ to %lx\n", ValueName, dw);
343 
344           NtSetValueKey (KeyHandle,
345                          ValueName,
346                          0,
347                          Type,
348                          (PVOID)&dw,
349                          sizeof(ULONG));
350         }
351       else
352         {
353           DPRINT("setting value %wZ to %S\n", ValueName, Str);
354 
355           if (Str)
356             {
357               NtSetValueKey (KeyHandle,
358                              ValueName,
359                              0,
360                              Type,
361                              (PVOID)Str,
362                              Size * sizeof(WCHAR));
363             }
364           else
365             {
366               NtSetValueKey (KeyHandle,
367                              ValueName,
368                              0,
369                              Type,
370                              (PVOID)&EmptyStr,
371                              sizeof(WCHAR));
372             }
373         }
374       RtlFreeHeap (ProcessHeap, 0, Str);
375     }
376   else  /* get the binary data */
377     {
378       PUCHAR Data = NULL;
379 
380       if (!SpInfGetBinaryField(Context, 5, NULL, 0, &Size))
381         Size = 0;
382 
383       if (Size)
384         {
385           Data = (unsigned char*) RtlAllocateHeap(ProcessHeap, 0, Size);
386           if (Data == NULL)
387             return FALSE;
388 
389           DPRINT("setting binary data %wZ len %lu\n", ValueName, Size);
390           SpInfGetBinaryField(Context, 5, Data, Size, NULL);
391         }
392 
393       NtSetValueKey (KeyHandle,
394                      ValueName,
395                      0,
396                      Type,
397                      (PVOID)Data,
398                      Size);
399 
400       RtlFreeHeap (ProcessHeap, 0, Data);
401     }
402 
403   return TRUE;
404 }
405 
406 /***********************************************************************
407  *            registry_callback
408  *
409  * Called once for each AddReg and DelReg entry in a given section.
410  */
411 static BOOLEAN
412 registry_callback(HINF hInf, PCWSTR Section, BOOLEAN Delete)
413 {
414     NTSTATUS Status;
415     OBJECT_ATTRIBUTES ObjectAttributes;
416     UNICODE_STRING Name, Value;
417     PUNICODE_STRING ValuePtr;
418     UINT Flags;
419     WCHAR Buffer[MAX_INF_STRING_LENGTH];
420 
421     INFCONTEXT Context;
422     PCWSTR RootKeyName;
423     HANDLE RootKeyHandle, KeyHandle;
424     BOOLEAN Ok;
425 
426     Ok = SpInfFindFirstLine(hInf, Section, NULL, &Context);
427     if (!Ok)
428         return TRUE; /* Don't fail if the section isn't present */
429 
430     for (;Ok; Ok = SpInfFindNextLine(&Context, &Context))
431     {
432         /* get root */
433         if (!SpInfGetStringField(&Context, 1, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
434             continue;
435         RootKeyHandle = GetRootKeyByName(Buffer, &RootKeyName);
436         if (!RootKeyHandle)
437             continue;
438 
439         /* get key */
440         if (!SpInfGetStringField(&Context, 2, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
441             *Buffer = 0;
442 
443         DPRINT("KeyName: <%S\\%S>\n", RootKeyName, Buffer);
444 
445         /* get flags */
446         if (!SpInfGetIntField(&Context, 4, (PINT)&Flags))
447             Flags = 0;
448 
449         DPRINT("Flags: %lx\n", Flags);
450 
451         RtlInitUnicodeString(&Name, Buffer);
452         InitializeObjectAttributes(&ObjectAttributes,
453                                    &Name,
454                                    OBJ_CASE_INSENSITIVE,
455                                    RootKeyHandle,
456                                    NULL);
457 
458         if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
459         {
460             Status = NtOpenKey(&KeyHandle,
461                                KEY_ALL_ACCESS,
462                                &ObjectAttributes);
463             if (!NT_SUCCESS(Status))
464             {
465                 DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
466                 continue;  /* ignore if it doesn't exist */
467             }
468         }
469         else
470         {
471             Status = CreateNestedKey(&KeyHandle,
472                                      KEY_ALL_ACCESS,
473                                      &ObjectAttributes,
474                                      REG_OPTION_NON_VOLATILE);
475             if (!NT_SUCCESS(Status))
476             {
477                 DPRINT1("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
478                 continue;
479             }
480         }
481 
482         /* get value name */
483         if (SpInfGetStringField(&Context, 3, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL))
484         {
485             RtlInitUnicodeString(&Value, Buffer);
486             ValuePtr = &Value;
487         }
488         else
489         {
490             ValuePtr = NULL;
491         }
492 
493         /* and now do it */
494         if (!do_reg_operation(KeyHandle, ValuePtr, &Context, Flags))
495         {
496             NtClose(KeyHandle);
497             return FALSE;
498         }
499 
500         NtClose(KeyHandle);
501     }
502 
503     return TRUE;
504 }
505 
506 BOOLEAN
507 ImportRegistryFile(
508     IN PCWSTR SourcePath,
509     IN PCWSTR FileName,
510     IN PCWSTR Section,
511     IN LCID LocaleId,
512     IN BOOLEAN Delete)
513 {
514     HINF hInf;
515     UINT ErrorLine;
516     WCHAR FileNameBuffer[MAX_PATH];
517 
518     /* Load the INF file from the installation media */
519     CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
520                  SourcePath, FileName);
521 
522     hInf = SpInfOpenInfFile(FileNameBuffer,
523                             NULL,
524                             INF_STYLE_WIN4,
525                             LocaleId,
526                             &ErrorLine);
527     if (hInf == INVALID_HANDLE_VALUE)
528     {
529         DPRINT1("SpInfOpenInfFile() failed\n");
530         return FALSE;
531     }
532 
533 #if 0
534     if (!registry_callback(hInf, L"DelReg", FALSE))
535     {
536         DPRINT1("registry_callback() failed\n");
537         SpInfCloseInfFile(hInf);
538         return FALSE;
539     }
540 #endif
541 
542     if (!registry_callback(hInf, L"AddReg", FALSE))
543     {
544         DPRINT1("registry_callback() failed\n");
545         SpInfCloseInfFile(hInf);
546         return FALSE;
547     }
548 
549     if (!registry_callback(hInf, L"AddReg.NT" INF_ARCH, FALSE))
550     {
551         DPRINT1("registry_callback() failed\n");
552         SpInfCloseInfFile(hInf);
553         return FALSE;
554     }
555 
556     SpInfCloseInfFile(hInf);
557     return TRUE;
558 }
559 
560 
561 typedef enum _HIVE_UPDATE_STATE
562 {
563     Create, // Create a new hive file and save possibly existing old one with a .old extension.
564     Repair, // Re-create a new hive file and save possibly existing old one with a .brk extension.
565     Update  // Hive update, do not need to be recreated.
566 } HIVE_UPDATE_STATE;
567 
568 typedef struct _HIVE_LIST_ENTRY
569 {
570     PCWSTR HiveName;            // HiveFileName;
571     PCWSTR HiveRegistryPath;    // HiveRegMountPoint;
572     HANDLE PredefKeyHandle;
573     PCWSTR RegSymLink;
574     HIVE_UPDATE_STATE State;
575     // PUCHAR SecurityDescriptor;
576     // ULONG  SecurityDescriptorLength;
577 } HIVE_LIST_ENTRY, *PHIVE_LIST_ENTRY;
578 
579 #define NUMBER_OF_STANDARD_REGISTRY_HIVES   3
580 
581 HIVE_LIST_ENTRY RegistryHives[/*NUMBER_OF_STANDARD_REGISTRY_HIVES*/] =
582 {
583     { L"SYSTEM"  , L"\\Registry\\Machine\\USetup_SYSTEM"  , HKEY_LOCAL_MACHINE, L"SYSTEM"  , Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
584     { L"SOFTWARE", L"\\Registry\\Machine\\USetup_SOFTWARE", HKEY_LOCAL_MACHINE, L"SOFTWARE", Create /* , SoftwareSecurity, sizeof(SoftwareSecurity) */ },
585     { L"DEFAULT" , L"\\Registry\\User\\USetup_DEFAULT"    , HKEY_USERS        , L".DEFAULT", Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
586 
587 //  { L"BCD"     , L"\\Registry\\Machine\\USetup_BCD", HKEY_LOCAL_MACHINE, L"BCD00000000", Create /* , BcdSecurity     , sizeof(BcdSecurity)      */ },
588 };
589 C_ASSERT(_countof(RegistryHives) == NUMBER_OF_STANDARD_REGISTRY_HIVES);
590 
591 #define NUMBER_OF_SECURITY_REGISTRY_HIVES   2
592 
593 /** These hives are created by LSASS during 2nd stage setup */
594 HIVE_LIST_ENTRY SecurityRegistryHives[/*NUMBER_OF_SECURITY_REGISTRY_HIVES*/] =
595 {
596     { L"SAM"     , L"\\Registry\\Machine\\USetup_SAM"     , HKEY_LOCAL_MACHINE, L"SAM"     , Create /* , SystemSecurity  , sizeof(SystemSecurity)   */ },
597     { L"SECURITY", L"\\Registry\\Machine\\USetup_SECURITY", HKEY_LOCAL_MACHINE, L"SECURITY", Create /* , NULL            , 0                        */ },
598 };
599 C_ASSERT(_countof(SecurityRegistryHives) == NUMBER_OF_SECURITY_REGISTRY_HIVES);
600 
601 
602 NTSTATUS
603 VerifyRegistryHives(
604     IN PUNICODE_STRING NtSystemRoot,
605     OUT PBOOLEAN ShouldRepairRegistry)
606 {
607     NTSTATUS Status;
608     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
609     UINT i;
610 
611     /* Suppose first the registry hives do not have to be fully recreated */
612     *ShouldRepairRegistry = FALSE;
613 
614     /* Acquire restore privilege */
615     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
616     if (!NT_SUCCESS(Status))
617     {
618         DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
619         /* Exit prematurely here.... */
620         return Status;
621     }
622 
623     /* Acquire backup privilege */
624     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
625     if (!NT_SUCCESS(Status))
626     {
627         DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
628         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
629         /* Exit prematurely here.... */
630         return Status;
631     }
632 
633     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
634     {
635         Status = VerifyRegistryHive(NtSystemRoot, RegistryHives[i].HiveName);
636         if (!NT_SUCCESS(Status))
637         {
638             DPRINT1("Registry hive '%S' needs repair!\n", RegistryHives[i].HiveName);
639             RegistryHives[i].State = Repair;
640             *ShouldRepairRegistry = TRUE;
641         }
642         else
643         {
644             RegistryHives[i].State = Update;
645         }
646     }
647 
648     /** These hives are created by LSASS during 2nd stage setup */
649     for (i = 0; i < ARRAYSIZE(SecurityRegistryHives); ++i)
650     {
651         Status = VerifyRegistryHive(NtSystemRoot, SecurityRegistryHives[i].HiveName);
652         if (!NT_SUCCESS(Status))
653         {
654             DPRINT1("Registry hive '%S' needs repair!\n", SecurityRegistryHives[i].HiveName);
655             SecurityRegistryHives[i].State = Repair;
656             /*
657              * Note that it's not the role of the 1st-stage installer to fix
658              * the security hives. This should be done at 2nd-stage installation
659              * by LSASS.
660              */
661         }
662         else
663         {
664             SecurityRegistryHives[i].State = Update;
665         }
666     }
667 
668     /* Reset the status (we succeeded in checking all the hives) */
669     Status = STATUS_SUCCESS;
670 
671     /* Remove restore and backup privileges */
672     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
673     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
674 
675     return Status;
676 }
677 
678 NTSTATUS
679 RegInitializeRegistry(
680     IN PUNICODE_STRING NtSystemRoot)
681 {
682     NTSTATUS Status;
683     HANDLE KeyHandle;
684     UNICODE_STRING KeyName;
685     OBJECT_ATTRIBUTES ObjectAttributes;
686     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
687     ULONG Disposition;
688     UINT i;
689 
690     /* Acquire restore privilege */
691     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
692     if (!NT_SUCCESS(Status))
693     {
694         DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
695         /* Exit prematurely here.... */
696         return Status;
697     }
698 
699     /* Acquire backup privilege */
700     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
701     if (!NT_SUCCESS(Status))
702     {
703         DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
704         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
705         /* Exit prematurely here.... */
706         return Status;
707     }
708 
709     /*
710      * Create the template proto-hive.
711      *
712      * Use a dummy root key name:
713      * - On 2k/XP/2k3, this is "$$$PROTO.HIV"
714      * - On Vista+, this is "CMI-CreateHive{guid}"
715      * See https://github.com/libyal/winreg-kb/blob/master/documentation/Registry%20files.asciidoc
716      * for more information.
717      */
718     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
719     InitializeObjectAttributes(&ObjectAttributes,
720                                &KeyName,
721                                OBJ_CASE_INSENSITIVE,
722                                NULL,
723                                NULL);
724     Status = NtCreateKey(&KeyHandle,
725                          KEY_ALL_ACCESS,
726                          &ObjectAttributes,
727                          0,
728                          NULL,
729                          REG_OPTION_NON_VOLATILE,
730                          NULL);
731     if (!NT_SUCCESS(Status))
732     {
733         DPRINT1("NtCreateKey() failed to create the proto-hive (Status %lx)\n", Status);
734         goto Quit;
735     }
736     NtFlushKey(KeyHandle);
737 
738     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
739     {
740         if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
741             continue;
742 
743         Status = CreateRegistryFile(NtSystemRoot,
744                                     RegistryHives[i].HiveName,
745                                     RegistryHives[i].State != Repair, // RegistryHives[i].State == Create,
746                                     KeyHandle);
747         if (!NT_SUCCESS(Status))
748         {
749             DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
750             /* Exit prematurely here.... */
751             /* That is now done, remove the proto-hive */
752             NtDeleteKey(KeyHandle);
753             NtClose(KeyHandle);
754             goto Quit;
755         }
756     }
757 
758     /* That is now done, remove the proto-hive */
759     NtDeleteKey(KeyHandle);
760     NtClose(KeyHandle);
761 
762 
763     /*
764      * Prepare the registry root keys. Since we cannot create real registry keys
765      * inside the master keys (\Registry, \Registry\Machine or \Registry\User),
766      * we need to perform some SymLink tricks instead.
767      */
768 
769     /* Our offline HKLM '\Registry\Machine' is inside '\Registry\Machine\SYSTEM\USetup_Machine' */
770     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].MountPoint);
771     InitializeObjectAttributes(&ObjectAttributes,
772                                &KeyName,
773                                OBJ_CASE_INSENSITIVE,
774                                NULL,
775                                NULL);
776     KeyHandle = NULL;
777     Status = NtCreateKey(&KeyHandle,
778                          KEY_ALL_ACCESS,
779                          &ObjectAttributes,
780                          0,
781                          NULL,
782                     // FIXME: Using REG_OPTION_VOLATILE works OK on Windows,
783                     // but I need to check whether it works OK on ReactOS too.
784                          REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE,
785                          &Disposition);
786     if (!NT_SUCCESS(Status))
787     {
788         DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
789         // return Status;
790     }
791     RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle = KeyHandle;
792 
793     /* Our offline HKU '\Registry\User' is inside '\Registry\Machine\SYSTEM\USetup_User' */
794     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_USERS)].MountPoint);
795     InitializeObjectAttributes(&ObjectAttributes,
796                                &KeyName,
797                                OBJ_CASE_INSENSITIVE,
798                                NULL,
799                                NULL);
800     KeyHandle = NULL;
801     Status = NtCreateKey(&KeyHandle,
802                          KEY_ALL_ACCESS,
803                          &ObjectAttributes,
804                          0,
805                          NULL,
806                     // FIXME: Using REG_OPTION_VOLATILE works OK on Windows,
807                     // but I need to check whether it works OK on ReactOS too.
808                          REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE,
809                          &Disposition);
810     if (!NT_SUCCESS(Status))
811     {
812         DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
813         // return Status;
814     }
815     RootKeys[GetPredefKeyIndex(HKEY_USERS)].Handle = KeyHandle;
816 
817 
818     /*
819      * Now properly mount the offline hive files
820      */
821     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
822     {
823         // if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
824             // continue;
825 
826         if (RegistryHives[i].State == Create || RegistryHives[i].State == Repair)
827         {
828             Status = ConnectRegistry(NULL,
829                                      RegistryHives[i].HiveRegistryPath,
830                                      NtSystemRoot,
831                                      RegistryHives[i].HiveName
832                                      /* SystemSecurity, sizeof(SystemSecurity) */);
833             if (!NT_SUCCESS(Status))
834             {
835                 DPRINT1("ConnectRegistry(%S) failed, Status 0x%08lx\n",
836                         RegistryHives[i].HiveName, Status);
837             }
838 
839             /* Create the registry symlink to this key */
840             Status = CreateSymLinkKey(RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
841                                       RegistryHives[i].RegSymLink,
842                                       RegistryHives[i].HiveRegistryPath);
843             if (!NT_SUCCESS(Status))
844             {
845                 DPRINT1("CreateSymLinkKey(%S) failed, Status 0x%08lx\n",
846                         RegistryHives[i].RegSymLink, Status);
847             }
848         }
849         else
850         {
851             /* Create *DUMMY* volatile hives just to make the update procedure working */
852 
853             RtlInitUnicodeString(&KeyName, RegistryHives[i].RegSymLink);
854             InitializeObjectAttributes(&ObjectAttributes,
855                                        &KeyName,
856                                        OBJ_CASE_INSENSITIVE,
857                                        RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
858                                        NULL);
859             KeyHandle = NULL;
860             Status = NtCreateKey(&KeyHandle,
861                                  KEY_ALL_ACCESS,
862                                  &ObjectAttributes,
863                                  0,
864                                  NULL,
865                             // FIXME: Using REG_OPTION_VOLATILE works OK on Windows,
866                             // but I need to check whether it works OK on ReactOS too.
867                                  REG_OPTION_NON_VOLATILE, // REG_OPTION_VOLATILE,
868                                  &Disposition);
869             if (!NT_SUCCESS(Status))
870             {
871                 DPRINT1("NtCreateKey(%wZ) failed (Status 0x%08lx)\n", &KeyName, Status);
872                 // return Status;
873             }
874             NtClose(KeyHandle);
875         }
876     }
877 
878 
879     /* HKCU is a handle to 'HKU\.DEFAULT' */
880 #if 0
881     RtlInitUnicodeString(&KeyName, L".DEFAULT");
882     InitializeObjectAttributes(&ObjectAttributes,
883                                &KeyName,
884                                OBJ_CASE_INSENSITIVE,
885                                RootKeys[GetPredefKeyIndex(HKEY_USERS)].Handle,
886                                NULL);
887 #else
888     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_CURRENT_USER)].MountPoint);
889     InitializeObjectAttributes(&ObjectAttributes,
890                                &KeyName,
891                                OBJ_CASE_INSENSITIVE,
892                                NULL,
893                                NULL);
894 #endif
895     KeyHandle = NULL;
896     Status = NtOpenKey(&KeyHandle,
897                        KEY_ALL_ACCESS,
898                        &ObjectAttributes);
899     if (!NT_SUCCESS(Status))
900     {
901         DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
902     }
903     RootKeys[GetPredefKeyIndex(HKEY_CURRENT_USER)].Handle = KeyHandle;
904 
905 
906     /* HKCR is a handle to 'HKLM\Software\Classes' */
907 #if 0
908     RtlInitUnicodeString(&KeyName, L"Software\\Classes");
909     InitializeObjectAttributes(&ObjectAttributes,
910                                &KeyName,
911                                OBJ_CASE_INSENSITIVE,
912                                RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
913                                NULL);
914 #else
915     RtlInitUnicodeString(&KeyName, RootKeys[GetPredefKeyIndex(HKEY_CLASSES_ROOT)].MountPoint);
916     InitializeObjectAttributes(&ObjectAttributes,
917                                &KeyName,
918                                OBJ_CASE_INSENSITIVE,
919                                NULL,
920                                NULL);
921 #endif
922     KeyHandle = NULL;
923     /* We use NtCreateKey instead of NtOpenKey because Software\Classes doesn't exist originally */
924     Status = NtCreateKey(&KeyHandle,
925                          KEY_ALL_ACCESS,
926                          &ObjectAttributes,
927                          0,
928                          NULL,
929                          REG_OPTION_NON_VOLATILE,
930                          &Disposition);
931     if (!NT_SUCCESS(Status))
932     {
933         DPRINT1("NtCreateKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
934     }
935     else
936     {
937         DPRINT("NtCreateKey() succeeded to %s the %wZ key (Status %lx)\n",
938                Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
939                &KeyName, Status);
940     }
941     RootKeys[GetPredefKeyIndex(HKEY_CLASSES_ROOT)].Handle = KeyHandle;
942 
943 
944     Status = STATUS_SUCCESS;
945 
946 
947     /* Create the 'HKLM\SYSTEM\ControlSet001' key */
948     // REGISTRY_SETUP_MACHINE L"SYSTEM\\ControlSet001"
949     RtlInitUnicodeString(&KeyName, L"SYSTEM\\ControlSet001");
950     InitializeObjectAttributes(&ObjectAttributes,
951                                &KeyName,
952                                OBJ_CASE_INSENSITIVE,
953                                RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
954                                NULL);
955     Status = NtCreateKey(&KeyHandle,
956                          KEY_ALL_ACCESS,
957                          &ObjectAttributes,
958                          0,
959                          NULL,
960                          REG_OPTION_NON_VOLATILE,
961                          &Disposition);
962     if (!NT_SUCCESS(Status))
963     {
964         DPRINT1("NtCreateKey() failed to create the ControlSet001 key (Status %lx)\n", Status);
965         // return Status;
966     }
967     else
968     {
969         DPRINT("NtCreateKey() succeeded to %s the ControlSet001 key (Status %lx)\n",
970                Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
971                Status);
972     }
973     NtClose(KeyHandle);
974 
975     /* Create the 'HKLM\SYSTEM\CurrentControlSet' symlink */
976     Status = CreateSymLinkKey(RootKeys[GetPredefKeyIndex(HKEY_LOCAL_MACHINE)].Handle,
977                               L"SYSTEM\\CurrentControlSet",
978                               REGISTRY_SETUP_MACHINE L"SYSTEM\\ControlSet001");
979     if (!NT_SUCCESS(Status))
980     {
981         DPRINT1("CreateSymLinkKey(CurrentControlSet) failed, Status 0x%08lx\n", Status);
982     }
983 
984 
985     Status = STATUS_SUCCESS;
986 
987 
988 Quit:
989     /* Remove restore and backup privileges */
990     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
991     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
992 
993     return Status;
994 }
995 
996 VOID
997 RegCleanupRegistry(
998     IN PUNICODE_STRING NtSystemRoot)
999 {
1000     NTSTATUS Status;
1001     HANDLE KeyHandle;
1002     UNICODE_STRING KeyName;
1003     OBJECT_ATTRIBUTES ObjectAttributes;
1004     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
1005     UINT i;
1006     WCHAR SrcPath[MAX_PATH];
1007     WCHAR DstPath[MAX_PATH];
1008 
1009     /* Acquire restore privilege */
1010     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
1011     if (!NT_SUCCESS(Status))
1012     {
1013         DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
1014         /* Exit prematurely here.... */
1015         return;
1016     }
1017 
1018     /* Acquire backup privilege */
1019     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
1020     if (!NT_SUCCESS(Status))
1021     {
1022         DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
1023         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
1024         /* Exit prematurely here.... */
1025         return;
1026     }
1027 
1028     /*
1029      * To keep the running system clean we need first to remove the symlinks
1030      * we have created and then unmounting the hives. Finally we delete the
1031      * master registry keys.
1032      */
1033 
1034     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
1035     {
1036         if (RegistryHives[i].State == Create || RegistryHives[i].State == Repair)
1037         {
1038             /* Delete the registry symlink to this key */
1039             Status = DeleteSymLinkKey(RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
1040                                       RegistryHives[i].RegSymLink);
1041             if (!NT_SUCCESS(Status))
1042             {
1043                 DPRINT1("DeleteSymLinkKey(%S) failed, Status 0x%08lx\n",
1044                         RegistryHives[i].RegSymLink, Status);
1045             }
1046 
1047             /* Unmount the hive */
1048             Status = DisconnectRegistry(NULL,
1049                                         RegistryHives[i].HiveRegistryPath,
1050                                         1 /* REG_FORCE_UNLOAD */);
1051             if (!NT_SUCCESS(Status))
1052             {
1053                 DPRINT1("Unmounting '%S' failed\n", RegistryHives[i].HiveRegistryPath);
1054             }
1055 
1056             /* Switch the hive state to 'Update' */
1057             RegistryHives[i].State = Update;
1058         }
1059         else
1060         {
1061             /* Delete the *DUMMY* volatile hives created for the update procedure */
1062 
1063             RtlInitUnicodeString(&KeyName, RegistryHives[i].RegSymLink);
1064             InitializeObjectAttributes(&ObjectAttributes,
1065                                        &KeyName,
1066                                        OBJ_CASE_INSENSITIVE,
1067                                        RootKeys[GetPredefKeyIndex(RegistryHives[i].PredefKeyHandle)].Handle,
1068                                        NULL);
1069             KeyHandle = NULL;
1070             Status = NtOpenKey(&KeyHandle,
1071                                DELETE,
1072                                &ObjectAttributes);
1073             if (!NT_SUCCESS(Status))
1074             {
1075                 DPRINT1("NtOpenKey(%wZ) failed, Status 0x%08lx\n", &KeyName, Status);
1076                 // return;
1077             }
1078 
1079             NtDeleteKey(KeyHandle);
1080             NtClose(KeyHandle);
1081         }
1082     }
1083 
1084     /*
1085      * FIXME: Once force-unloading keys is correctly fixed, I'll fix
1086      * this code that closes some of the registry keys that were opened
1087      * inside the hives we've just unmounted above...
1088      */
1089 
1090     /* Remove the registry root keys */
1091     for (i = 0; i < ARRAYSIZE(RootKeys); ++i)
1092     {
1093         if (RootKeys[i].Handle)
1094         {
1095             /**/NtFlushKey(RootKeys[i].Handle);/**/ // FIXME: Why does it hang? Answer: because we have some problems in CMAPI!
1096             NtDeleteKey(RootKeys[i].Handle);
1097             NtClose(RootKeys[i].Handle);
1098             RootKeys[i].Handle = NULL;
1099         }
1100     }
1101 
1102     //
1103     // RegBackupRegistry()
1104     //
1105     /* Now backup the hives into .sav files */
1106     for (i = 0; i < ARRAYSIZE(RegistryHives); ++i)
1107     {
1108         if (RegistryHives[i].State != Create && RegistryHives[i].State != Repair)
1109             continue;
1110 
1111         CombinePaths(SrcPath, ARRAYSIZE(SrcPath), 3,
1112                      NtSystemRoot->Buffer, L"System32\\config", RegistryHives[i].HiveName);
1113         RtlStringCchCopyW(DstPath, ARRAYSIZE(DstPath), SrcPath);
1114         RtlStringCchCatW(DstPath, ARRAYSIZE(DstPath), L".sav");
1115 
1116         DPRINT1("Copy hive: %S ==> %S\n", SrcPath, DstPath);
1117         Status = SetupCopyFile(SrcPath, DstPath, FALSE);
1118         if (!NT_SUCCESS(Status))
1119         {
1120             DPRINT1("SetupCopyFile() failed (Status %lx)\n", Status);
1121             // return Status;
1122         }
1123     }
1124 
1125     /* Remove restore and backup privileges */
1126     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
1127     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
1128 }
1129 
1130 /* EOF */
1131