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