Patch by Jonathon Wilson:
[reactos.git] / reactos / 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
194 if (Flags & FLG_ADDREG_DELVAL) /* deletion */
195 {
196 #if 0
197 if (ValueName)
198 {
199 RegDeleteValueW( hkey, value );
200 }
201 else
202 {
203 RegDeleteKeyW( hkey, NULL );
204 }
205 #endif
206 return TRUE;
207 }
208
209 if (Flags & FLG_ADDREG_KEYONLY)
210 return TRUE;
211
212 #if 0
213 if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
214 {
215 BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
216 if (exists && (flags & FLG_ADDREG_NOCLOBBER))
217 return TRUE;
218 if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY))
219 return TRUE;
220 }
221 #endif
222
223 switch (Flags & FLG_ADDREG_TYPE_MASK)
224 {
225 case FLG_ADDREG_TYPE_SZ:
226 Type = REG_SZ;
227 break;
228
229 case FLG_ADDREG_TYPE_MULTI_SZ:
230 Type = REG_MULTI_SZ;
231 break;
232
233 case FLG_ADDREG_TYPE_EXPAND_SZ:
234 Type = REG_EXPAND_SZ;
235 break;
236
237 case FLG_ADDREG_TYPE_BINARY:
238 Type = REG_BINARY;
239 break;
240
241 case FLG_ADDREG_TYPE_DWORD:
242 Type = REG_DWORD;
243 break;
244
245 case FLG_ADDREG_TYPE_NONE:
246 Type = REG_NONE;
247 break;
248
249 default:
250 Type = Flags >> 16;
251 break;
252 }
253
254 if (!(Flags & FLG_ADDREG_BINVALUETYPE) ||
255 (Type == REG_DWORD && InfGetFieldCount (Context) == 5))
256 {
257 PWCHAR Str = NULL;
258
259 if (Type == REG_MULTI_SZ)
260 {
261 if (!InfGetMultiSzField (Context, 5, NULL, 0, &Size))
262 Size = 0;
263
264 if (Size)
265 {
266 Str = RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
267 if (Str == NULL)
268 return FALSE;
269
270 InfGetMultiSzField (Context, 5, Str, Size, NULL);
271 }
272
273 if (Flags & FLG_ADDREG_APPEND)
274 {
275 if (Str == NULL)
276 return TRUE;
277
278 // append_multi_sz_value( hkey, value, str, size );
279
280 RtlFreeHeap (ProcessHeap, 0, Str);
281 return TRUE;
282 }
283 /* else fall through to normal string handling */
284 }
285 else
286 {
287 if (!InfGetStringField (Context, 5, NULL, 0, &Size))
288 Size = 0;
289
290 if (Size)
291 {
292 Str = RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
293 if (Str == NULL)
294 return FALSE;
295
296 InfGetStringField (Context, 5, Str, Size, NULL);
297 }
298 }
299
300 if (Type == REG_DWORD)
301 {
302 ULONG dw = Str ? wcstol (Str, NULL, 0) : 0;
303
304 DPRINT("setting dword %wZ to %lx\n", ValueName, dw);
305
306 NtSetValueKey (KeyHandle,
307 ValueName,
308 0,
309 Type,
310 (PVOID)&dw,
311 sizeof(ULONG));
312 }
313 else
314 {
315 DPRINT("setting value %wZ to %S\n", ValueName, Str);
316
317 if (Str)
318 {
319 NtSetValueKey (KeyHandle,
320 ValueName,
321 0,
322 Type,
323 (PVOID)Str,
324 Size * sizeof(WCHAR));
325 }
326 else
327 {
328 NtSetValueKey (KeyHandle,
329 ValueName,
330 0,
331 Type,
332 (PVOID)&EmptyStr,
333 sizeof(WCHAR));
334 }
335 }
336 RtlFreeHeap (ProcessHeap, 0, Str);
337 }
338 else /* get the binary data */
339 {
340 PUCHAR Data = NULL;
341
342 if (!InfGetBinaryField (Context, 5, NULL, 0, &Size))
343 Size = 0;
344
345 if (Size)
346 {
347 Data = RtlAllocateHeap (ProcessHeap, 0, Size);
348 if (Data == NULL)
349 return FALSE;
350
351 DPRINT("setting binary data %wZ len %lu\n", ValueName, Size);
352 InfGetBinaryField (Context, 5, Data, Size, NULL);
353 }
354
355 NtSetValueKey (KeyHandle,
356 ValueName,
357 0,
358 Type,
359 (PVOID)Data,
360 Size);
361
362 RtlFreeHeap (ProcessHeap, 0, Data);
363 }
364
365 return TRUE;
366 }
367
368
369 NTSTATUS
370 CreateNestedKey (PHANDLE KeyHandle,
371 ACCESS_MASK DesiredAccess,
372 POBJECT_ATTRIBUTES ObjectAttributes)
373 {
374 OBJECT_ATTRIBUTES LocalObjectAttributes;
375 UNICODE_STRING LocalKeyName;
376 ULONG Disposition;
377 NTSTATUS Status;
378 ULONG FullNameLength;
379 ULONG Length;
380 PWCHAR Ptr;
381 HANDLE LocalKeyHandle;
382
383 Status = NtCreateKey (KeyHandle,
384 KEY_ALL_ACCESS,
385 ObjectAttributes,
386 0,
387 NULL,
388 0,
389 &Disposition);
390 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
391 if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
392 return Status;
393
394 /* Copy object attributes */
395 RtlCopyMemory (&LocalObjectAttributes,
396 ObjectAttributes,
397 sizeof(OBJECT_ATTRIBUTES));
398 RtlCreateUnicodeString (&LocalKeyName,
399 ObjectAttributes->ObjectName->Buffer);
400 LocalObjectAttributes.ObjectName = &LocalKeyName;
401 FullNameLength = LocalKeyName.Length / sizeof(WCHAR);
402
403 /* Remove the last part of the key name and try to create the key again. */
404 while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
405 {
406 Ptr = wcsrchr (LocalKeyName.Buffer, '\\');
407 if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
408 {
409 Status = STATUS_UNSUCCESSFUL;
410 break;
411 }
412 *Ptr = (WCHAR)0;
413 LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
414
415 Status = NtCreateKey (&LocalKeyHandle,
416 KEY_ALL_ACCESS,
417 &LocalObjectAttributes,
418 0,
419 NULL,
420 0,
421 &Disposition);
422 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
423 }
424
425 if (!NT_SUCCESS(Status))
426 {
427 RtlFreeUnicodeString (&LocalKeyName);
428 return Status;
429 }
430
431 /* Add removed parts of the key name and create them too. */
432 Length = wcslen (LocalKeyName.Buffer);
433 while (TRUE)
434 {
435 if (Length == FullNameLength)
436 {
437 Status = STATUS_SUCCESS;
438 *KeyHandle = LocalKeyHandle;
439 break;
440 }
441 NtClose (LocalKeyHandle);
442
443 LocalKeyName.Buffer[Length] = L'\\';
444 Length = wcslen (LocalKeyName.Buffer);
445 LocalKeyName.Length = Length * sizeof(WCHAR);
446
447 Status = NtCreateKey (&LocalKeyHandle,
448 KEY_ALL_ACCESS,
449 &LocalObjectAttributes,
450 0,
451 NULL,
452 0,
453 &Disposition);
454 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
455 if (!NT_SUCCESS(Status))
456 break;
457 }
458
459 RtlFreeUnicodeString (&LocalKeyName);
460
461 return Status;
462 }
463
464
465 /***********************************************************************
466 * registry_callback
467 *
468 * Called once for each AddReg and DelReg entry in a given section.
469 */
470 static BOOLEAN
471 registry_callback (HINF hInf, PCWSTR Section, BOOLEAN Delete)
472 {
473 OBJECT_ATTRIBUTES ObjectAttributes;
474 WCHAR Buffer[MAX_INF_STRING_LENGTH];
475 UNICODE_STRING Name;
476 UNICODE_STRING Value;
477 PUNICODE_STRING ValuePtr;
478 NTSTATUS Status;
479 ULONG Flags;
480 ULONG Length;
481
482 INFCONTEXT Context;
483 HANDLE KeyHandle;
484 BOOLEAN Ok;
485
486
487 Ok = InfFindFirstLine (hInf, Section, NULL, &Context);
488
489 for (;Ok; Ok = InfFindNextLine (&Context, &Context))
490 {
491 /* get root */
492 if (!InfGetStringField (&Context, 1, Buffer, MAX_INF_STRING_LENGTH, NULL))
493 continue;
494 if (!GetRootKey (Buffer))
495 continue;
496
497 /* get key */
498 Length = wcslen (Buffer);
499 if (!InfGetStringField (&Context, 2, Buffer + Length, MAX_INF_STRING_LENGTH - Length, NULL))
500 *Buffer = 0;
501
502 DPRINT("KeyName: <%S>\n", Buffer);
503
504 /* get flags */
505 if (!InfGetIntField (&Context, 4, (PLONG)&Flags))
506 Flags = 0;
507
508 DPRINT("Flags: %lx\n", Flags);
509
510 RtlInitUnicodeString (&Name,
511 Buffer);
512
513 InitializeObjectAttributes (&ObjectAttributes,
514 &Name,
515 OBJ_CASE_INSENSITIVE,
516 NULL,
517 NULL);
518
519 if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
520 {
521 Status = NtOpenKey (&KeyHandle,
522 KEY_ALL_ACCESS,
523 &ObjectAttributes);
524 if (!NT_SUCCESS(Status))
525 {
526 DPRINT("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
527 continue; /* ignore if it doesn't exist */
528 }
529 }
530 else
531 {
532 Status = CreateNestedKey (&KeyHandle,
533 KEY_ALL_ACCESS,
534 &ObjectAttributes);
535 if (!NT_SUCCESS(Status))
536 {
537 DPRINT("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
538 continue;
539 }
540 }
541
542 /* get value name */
543 if (InfGetStringField (&Context, 3, Buffer, MAX_INF_STRING_LENGTH, NULL))
544 {
545 RtlInitUnicodeString (&Value,
546 Buffer);
547 ValuePtr = &Value;
548 }
549 else
550 {
551 ValuePtr = NULL;
552 }
553
554 /* and now do it */
555 if (!do_reg_operation (KeyHandle, ValuePtr, &Context, Flags))
556 {
557 NtClose (KeyHandle);
558 return FALSE;
559 }
560
561 NtClose (KeyHandle);
562 }
563
564 return TRUE;
565 }
566
567
568 BOOLEAN
569 ImportRegistryFile(PWSTR Filename,
570 PWSTR Section,
571 BOOLEAN Delete)
572 {
573 WCHAR FileNameBuffer[MAX_PATH];
574 UNICODE_STRING FileName;
575 HINF hInf;
576 NTSTATUS Status;
577 ULONG ErrorLine;
578
579 /* Load inf file from install media. */
580 wcscpy(FileNameBuffer, SourceRootPath.Buffer);
581 wcscat(FileNameBuffer, L"\\reactos\\");
582 wcscat(FileNameBuffer, Filename);
583
584 RtlInitUnicodeString(&FileName,
585 FileNameBuffer);
586
587 Status = InfOpenFile(&hInf,
588 &FileName,
589 &ErrorLine);
590 if (!NT_SUCCESS(Status))
591 {
592 DPRINT1("InfOpenFile() failed (Status %lx)\n", Status);
593 return FALSE;
594 }
595
596 if (!registry_callback (hInf, L"AddReg", FALSE))
597 {
598 DPRINT1("registry_callback() failed\n");
599 }
600
601 InfCloseFile (hInf);
602
603 return TRUE;
604 }
605
606
607 BOOLEAN
608 SetInstallPathValue(PUNICODE_STRING InstallPath)
609 {
610 OBJECT_ATTRIBUTES ObjectAttributes;
611 UNICODE_STRING KeyName;
612 UNICODE_STRING ValueName;
613 HANDLE KeyHandle;
614 NTSTATUS Status;
615
616 /* Create the 'secret' InstallPath key */
617 RtlInitUnicodeStringFromLiteral (&KeyName,
618 L"\\Registry\\Machine\\HARDWARE");
619 InitializeObjectAttributes (&ObjectAttributes,
620 &KeyName,
621 OBJ_CASE_INSENSITIVE,
622 NULL,
623 NULL);
624 Status = NtOpenKey (&KeyHandle,
625 KEY_ALL_ACCESS,
626 &ObjectAttributes);
627 if (!NT_SUCCESS(Status))
628 {
629 DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
630 return FALSE;
631 }
632
633 RtlInitUnicodeStringFromLiteral (&ValueName,
634 L"InstallPath");
635 Status = NtSetValueKey (KeyHandle,
636 &ValueName,
637 0,
638 REG_SZ,
639 (PVOID)InstallPath->Buffer,
640 InstallPath->Length);
641 NtClose(KeyHandle);
642 if (!NT_SUCCESS(Status))
643 {
644 DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
645 return FALSE;
646 }
647
648 return TRUE;
649 }
650
651 /* EOF */