update for HEAD-2003091401
[reactos.git] / subsys / system / usetup / registry.c
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
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            subsys/system/usetup/registry.c
23  * PURPOSE:         Registry creation functions
24  * PROGRAMMER:      Eric Kohl
25  */
26
27 /* INCLUDES *****************************************************************/
28
29 #include <ddk/ntddk.h>
30 #include <ntdll/rtl.h>
31
32 #include "usetup.h"
33 #include "registry.h"
34 #include "infcache.h"
35
36
37
38 #define FLG_ADDREG_BINVALUETYPE           0x00000001
39 #define FLG_ADDREG_NOCLOBBER              0x00000002
40 #define FLG_ADDREG_DELVAL                 0x00000004
41 #define FLG_ADDREG_APPEND                 0x00000008
42 #define FLG_ADDREG_KEYONLY                0x00000010
43 #define FLG_ADDREG_OVERWRITEONLY          0x00000020
44 #define FLG_ADDREG_TYPE_SZ                0x00000000
45 #define FLG_ADDREG_TYPE_MULTI_SZ          0x00010000
46 #define FLG_ADDREG_TYPE_EXPAND_SZ         0x00020000
47 #define FLG_ADDREG_TYPE_BINARY           (0x00000000 | FLG_ADDREG_BINVALUETYPE)
48 #define FLG_ADDREG_TYPE_DWORD            (0x00010000 | FLG_ADDREG_BINVALUETYPE)
49 #define FLG_ADDREG_TYPE_NONE             (0x00020000 | FLG_ADDREG_BINVALUETYPE)
50 #define FLG_ADDREG_TYPE_MASK             (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
51
52
53 /* FUNCTIONS ****************************************************************/
54
55 static BOOLEAN
56 GetRootKey (PWCHAR Name)
57 {
58   if (!_wcsicmp (Name, L"HKCR"))
59     {
60       wcscpy (Name, L"\\Registry\\Machine\\SOFTWARE\\Classes\\");
61       return TRUE;
62     }
63
64   if (!_wcsicmp (Name, L"HKCU"))
65     {
66       wcscpy (Name, L"\\Registry\\User\\.DEFAULT\\");
67       return TRUE;
68     }
69
70   if (!_wcsicmp (Name, L"HKLM"))
71     {
72       wcscpy (Name, L"\\Registry\\Machine\\");
73       return TRUE;
74     }
75
76   if (!_wcsicmp (Name, L"HKU"))
77     {
78       wcscpy (Name, L"\\Registry\\User\\");
79       return TRUE;
80     }
81
82 #if 0
83   if (!_wcsicmp (Name, L"HKR"))
84     return FALSE;
85 #endif
86
87   return FALSE;
88 }
89
90
91 /***********************************************************************
92  *            append_multi_sz_value
93  *
94  * Append a multisz string to a multisz registry value.
95  */
96 #if 0
97 static void
98 append_multi_sz_value (HANDLE hkey,
99                        const WCHAR *value,
100                        const WCHAR *strings,
101                        DWORD str_size )
102 {
103     DWORD size, type, total;
104     WCHAR *buffer, *p;
105
106     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
107     if (type != REG_MULTI_SZ) return;
108
109     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
110     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
111
112     /* compare each string against all the existing ones */
113     total = size;
114     while (*strings)
115     {
116         int len = strlenW(strings) + 1;
117
118         for (p = buffer; *p; p += strlenW(p) + 1)
119             if (!strcmpiW( p, strings )) break;
120
121         if (!*p)  /* not found, need to append it */
122         {
123             memcpy( p, strings, len * sizeof(WCHAR) );
124             p[len] = 0;
125             total += len;
126         }
127         strings += len;
128     }
129     if (total != size)
130     {
131         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
132         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
133     }
134  done:
135     HeapFree( GetProcessHeap(), 0, buffer );
136 }
137 #endif
138
139 /***********************************************************************
140  *            delete_multi_sz_value
141  *
142  * Remove a string from a multisz registry value.
143  */
144 #if 0
145 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
146 {
147     DWORD size, type;
148     WCHAR *buffer, *src, *dst;
149
150     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
151     if (type != REG_MULTI_SZ) return;
152     /* allocate double the size, one for value before and one for after */
153     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
154     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
155     src = buffer;
156     dst = buffer + size;
157     while (*src)
158     {
159         int len = strlenW(src) + 1;
160         if (strcmpiW( src, string ))
161         {
162             memcpy( dst, src, len * sizeof(WCHAR) );
163             dst += len;
164         }
165         src += len;
166     }
167     *dst++ = 0;
168     if (dst != buffer + 2*size)  /* did we remove something? */
169     {
170         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
171         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
172                         (BYTE *)(buffer + size), dst - (buffer + size) );
173     }
174  done:
175     HeapFree( GetProcessHeap(), 0, buffer );
176 }
177 #endif
178
179 /***********************************************************************
180  *            do_reg_operation
181  *
182  * Perform an add/delete registry operation depending on the flags.
183  */
184 static BOOLEAN
185 do_reg_operation(HANDLE KeyHandle,
186                  PUNICODE_STRING ValueName,
187                  PINFCONTEXT Context,
188                  ULONG Flags)
189 {
190   WCHAR EmptyStr = (WCHAR)0;
191   ULONG Type;
192   ULONG Size;
193   NTSTATUS Status;
194
195   if (Flags & FLG_ADDREG_DELVAL)  /* deletion */
196     {
197 #if 0
198       if (ValueName)
199         {
200           RegDeleteValueW( hkey, value );
201         }
202       else
203         {
204           RegDeleteKeyW( hkey, NULL );
205         }
206 #endif
207       return TRUE;
208     }
209
210   if (Flags & FLG_ADDREG_KEYONLY)
211     return TRUE;
212
213 #if 0
214   if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
215     {
216       BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
217       if (exists && (flags & FLG_ADDREG_NOCLOBBER))
218         return TRUE;
219       if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY))
220         return TRUE;
221     }
222 #endif
223
224   switch (Flags & FLG_ADDREG_TYPE_MASK)
225     {
226       case FLG_ADDREG_TYPE_SZ:
227         Type = REG_SZ;
228         break;
229
230       case FLG_ADDREG_TYPE_MULTI_SZ:
231         Type = REG_MULTI_SZ;
232         break;
233
234       case FLG_ADDREG_TYPE_EXPAND_SZ:
235         Type = REG_EXPAND_SZ;
236         break;
237
238       case FLG_ADDREG_TYPE_BINARY:
239         Type = REG_BINARY;
240         break;
241
242       case FLG_ADDREG_TYPE_DWORD:
243         Type = REG_DWORD;
244         break;
245
246       case FLG_ADDREG_TYPE_NONE:
247         Type = REG_NONE;
248         break;
249
250       default:
251         Type = Flags >> 16;
252         break;
253     }
254
255   if (!(Flags & FLG_ADDREG_BINVALUETYPE) ||
256       (Type == REG_DWORD && InfGetFieldCount (Context) == 5))
257     {
258       PWCHAR Str = NULL;
259
260       if (Type == REG_MULTI_SZ)
261         {
262           if (!InfGetMultiSzField (Context, 5, NULL, 0, &Size))
263             Size = 0;
264
265           if (Size)
266             {
267               Str = RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
268               if (Str == NULL)
269                 return FALSE;
270
271               InfGetMultiSzField (Context, 5, Str, Size, NULL);
272             }
273
274           if (Flags & FLG_ADDREG_APPEND)
275             {
276               if (Str == NULL)
277                 return TRUE;
278
279 //            append_multi_sz_value( hkey, value, str, size );
280
281               RtlFreeHeap (ProcessHeap, 0, Str);
282               return TRUE;
283             }
284           /* else fall through to normal string handling */
285         }
286       else
287         {
288           if (!InfGetStringField (Context, 5, NULL, 0, &Size))
289             Size = 0;
290
291           if (Size)
292             {
293               Str = RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
294               if (Str == NULL)
295                 return FALSE;
296
297               InfGetStringField (Context, 5, Str, Size, NULL);
298             }
299         }
300
301       if (Type == REG_DWORD)
302         {
303           ULONG dw = Str ? wcstol (Str, NULL, 0) : 0;
304
305           DPRINT("setting dword %wZ to %lx\n", ValueName, dw);
306
307           NtSetValueKey (KeyHandle,
308                          ValueName,
309                          0,
310                          Type,
311                          (PVOID)&dw,
312                          sizeof(ULONG));
313         }
314       else
315         {
316           DPRINT("setting value %wZ to %S\n", ValueName, Str);
317
318           if (Str)
319             {
320               NtSetValueKey (KeyHandle,
321                              ValueName,
322                              0,
323                              Type,
324                              (PVOID)Str,
325                              Size * sizeof(WCHAR));
326             }
327           else
328             {
329               NtSetValueKey (KeyHandle,
330                              ValueName,
331                              0,
332                              Type,
333                              (PVOID)&EmptyStr,
334                              sizeof(WCHAR));
335             }
336         }
337       RtlFreeHeap (ProcessHeap, 0, Str);
338     }
339   else  /* get the binary data */
340     {
341       PUCHAR Data = NULL;
342
343       if (!InfGetBinaryField (Context, 5, NULL, 0, &Size))
344         Size = 0;
345
346       if (Size)
347         {
348           Data = RtlAllocateHeap (ProcessHeap, 0, Size);
349           if (Data == NULL)
350             return FALSE;
351
352           DPRINT("setting binary data %wZ len %lu\n", ValueName, Size);
353           InfGetBinaryField (Context, 5, Data, Size, NULL);
354         }
355
356       NtSetValueKey (KeyHandle,
357                      ValueName,
358                      0,
359                      Type,
360                      (PVOID)Data,
361                      Size);
362
363       RtlFreeHeap (ProcessHeap, 0, Data);
364     }
365
366   return TRUE;
367 }
368
369
370 NTSTATUS
371 CreateNestedKey (PHANDLE KeyHandle,
372                  ACCESS_MASK DesiredAccess,
373                  POBJECT_ATTRIBUTES ObjectAttributes)
374 {
375   OBJECT_ATTRIBUTES LocalObjectAttributes;
376   UNICODE_STRING LocalKeyName;
377   ULONG Disposition;
378   NTSTATUS Status;
379   ULONG FullNameLength;
380   ULONG Length;
381   PWCHAR Ptr;
382   HANDLE LocalKeyHandle;
383
384   Status = NtCreateKey (KeyHandle,
385                         KEY_ALL_ACCESS,
386                         ObjectAttributes,
387                         0,
388                         NULL,
389                         0,
390                         &Disposition);
391   DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
392   if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
393     return Status;
394
395   /* Copy object attributes */
396   RtlCopyMemory (&LocalObjectAttributes,
397                  ObjectAttributes,
398                  sizeof(OBJECT_ATTRIBUTES));
399   RtlCreateUnicodeString (&LocalKeyName,
400                           ObjectAttributes->ObjectName->Buffer);
401   LocalObjectAttributes.ObjectName = &LocalKeyName;
402   FullNameLength = LocalKeyName.Length / sizeof(WCHAR);
403
404   /* Remove the last part of the key name and try to create the key again. */
405   while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
406     {
407       Ptr = wcsrchr (LocalKeyName.Buffer, '\\');
408       if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
409         {
410           Status = STATUS_UNSUCCESSFUL;
411           break;
412         }
413       *Ptr = (WCHAR)0;
414       LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
415
416       Status = NtCreateKey (&LocalKeyHandle,
417                             KEY_ALL_ACCESS,
418                             &LocalObjectAttributes,
419                             0,
420                             NULL,
421                             0,
422                             &Disposition);
423       DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
424     }
425
426   if (!NT_SUCCESS(Status))
427     {
428       RtlFreeUnicodeString (&LocalKeyName);
429       return Status;
430     }
431
432   /* Add removed parts of the key name and create them too. */
433   Length = wcslen (LocalKeyName.Buffer);
434   while (TRUE)
435     {
436       if (Length == FullNameLength)
437         {
438           Status == STATUS_SUCCESS;
439           *KeyHandle = LocalKeyHandle;
440           break;
441         }
442       NtClose (LocalKeyHandle);
443
444       LocalKeyName.Buffer[Length] = L'\\';
445       Length = wcslen (LocalKeyName.Buffer);
446       LocalKeyName.Length = Length * sizeof(WCHAR);
447
448       Status = NtCreateKey (&LocalKeyHandle,
449                             KEY_ALL_ACCESS,
450                             &LocalObjectAttributes,
451                             0,
452                             NULL,
453                             0,
454                             &Disposition);
455       DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
456       if (!NT_SUCCESS(Status))
457         break;
458     }
459
460   RtlFreeUnicodeString (&LocalKeyName);
461
462   return Status;
463 }
464
465
466 /***********************************************************************
467  *            registry_callback
468  *
469  * Called once for each AddReg and DelReg entry in a given section.
470  */
471 static BOOLEAN
472 registry_callback (HINF hInf, PCWSTR Section, BOOLEAN Delete)
473 {
474   OBJECT_ATTRIBUTES ObjectAttributes;
475   WCHAR Buffer[MAX_INF_STRING_LENGTH];
476   UNICODE_STRING Name;
477   UNICODE_STRING Value;
478   PUNICODE_STRING ValuePtr;
479   NTSTATUS Status;
480   ULONG Flags;
481   ULONG Length;
482
483   INFCONTEXT Context;
484   HANDLE KeyHandle;
485   BOOLEAN Ok;
486
487
488   Ok = InfFindFirstLine (hInf, Section, NULL, &Context);
489
490   for (;Ok; Ok = InfFindNextLine (&Context, &Context))
491     {
492       /* get root */
493       if (!InfGetStringField (&Context, 1, Buffer, MAX_INF_STRING_LENGTH, NULL))
494         continue;
495       if (!GetRootKey (Buffer))
496         continue;
497
498       /* get key */
499       Length = wcslen (Buffer);
500       if (!InfGetStringField (&Context, 2, Buffer + Length, MAX_INF_STRING_LENGTH - Length, NULL))
501         *Buffer = 0;
502
503       DPRINT("KeyName: <%S>\n", Buffer);
504
505       /* get flags */
506       if (!InfGetIntField (&Context, 4, (PLONG)&Flags))
507         Flags = 0;
508
509       DPRINT("Flags: %lx\n", Flags);
510
511       RtlInitUnicodeString (&Name,
512                             Buffer);
513
514       InitializeObjectAttributes (&ObjectAttributes,
515                                   &Name,
516                                   OBJ_CASE_INSENSITIVE,
517                                   NULL,
518                                   NULL);
519
520       if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
521         {
522           Status = NtOpenKey (&KeyHandle,
523                               KEY_ALL_ACCESS,
524                               &ObjectAttributes);
525           if (!NT_SUCCESS(Status))
526             {
527               DPRINT("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
528               continue;  /* ignore if it doesn't exist */
529             }
530         }
531       else
532         {
533           Status = CreateNestedKey (&KeyHandle,
534                                     KEY_ALL_ACCESS,
535                                     &ObjectAttributes);
536           if (!NT_SUCCESS(Status))
537             {
538               DPRINT("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
539               continue;
540             }
541         }
542
543       /* get value name */
544       if (InfGetStringField (&Context, 3, Buffer, MAX_INF_STRING_LENGTH, NULL))
545         {
546           RtlInitUnicodeString (&Value,
547                                 Buffer);
548           ValuePtr = &Value;
549         }
550       else
551         {
552           ValuePtr = NULL;
553         }
554
555       /* and now do it */
556       if (!do_reg_operation (KeyHandle, ValuePtr, &Context, Flags))
557         {
558           NtClose (KeyHandle);
559           return FALSE;
560         }
561
562       NtClose (KeyHandle);
563     }
564
565   return TRUE;
566 }
567
568
569 BOOLEAN
570 ImportRegistryFile(PWSTR Filename,
571                    PWSTR Section,
572                    BOOLEAN Delete)
573 {
574   WCHAR FileNameBuffer[MAX_PATH];
575   UNICODE_STRING FileName;
576   HINF hInf;
577   NTSTATUS Status;
578   ULONG ErrorLine;
579
580   /* Load inf file from install media. */
581   wcscpy(FileNameBuffer, SourceRootPath.Buffer);
582   wcscat(FileNameBuffer, L"\\reactos\\");
583   wcscat(FileNameBuffer, Filename);
584
585   RtlInitUnicodeString(&FileName,
586                        FileNameBuffer);
587
588   Status = InfOpenFile(&hInf,
589                        &FileName,
590                        &ErrorLine);
591   if (!NT_SUCCESS(Status))
592     {
593       DPRINT1("InfOpenFile() failed (Status %lx)\n", Status);
594       return FALSE;
595     }
596
597   if (!registry_callback (hInf, L"AddReg", FALSE))
598     {
599       DPRINT1("registry_callback() failed\n");
600     }
601
602   InfCloseFile (hInf);
603
604   return TRUE;
605 }
606
607
608 BOOLEAN
609 SetInstallPathValue(PUNICODE_STRING InstallPath)
610 {
611   OBJECT_ATTRIBUTES ObjectAttributes;
612   UNICODE_STRING KeyName;
613   UNICODE_STRING ValueName;
614   HANDLE KeyHandle;
615   NTSTATUS Status;
616
617   /* Create the 'secret' InstallPath key */
618   RtlInitUnicodeStringFromLiteral (&KeyName,
619                                    L"\\Registry\\Machine\\HARDWARE");
620   InitializeObjectAttributes (&ObjectAttributes,
621                               &KeyName,
622                               OBJ_CASE_INSENSITIVE,
623                               NULL,
624                               NULL);
625   Status =  NtOpenKey (&KeyHandle,
626                        KEY_ALL_ACCESS,
627                        &ObjectAttributes);
628   if (!NT_SUCCESS(Status))
629     {
630       DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
631       return FALSE;
632     }
633
634   RtlInitUnicodeStringFromLiteral (&ValueName,
635                                    L"InstallPath");
636   Status = NtSetValueKey (KeyHandle,
637                           &ValueName,
638                           0,
639                           REG_SZ,
640                           (PVOID)InstallPath->Buffer,
641                           InstallPath->Length);
642   NtClose(KeyHandle);
643   if (!NT_SUCCESS(Status))
644     {
645       DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
646       return FALSE;
647     }
648
649   return TRUE;
650 }
651
652 /* EOF */