* Sync with recent trunk (r52637).
[reactos.git] / 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 USHORT FullNameLength;
402 PWCHAR Ptr;
403 HANDLE LocalKeyHandle;
404
405 Status = NtCreateKey (KeyHandle,
406 KEY_ALL_ACCESS,
407 ObjectAttributes,
408 0,
409 NULL,
410 0,
411 &Disposition);
412 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
413 if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
414 return Status;
415
416 /* Copy object attributes */
417 RtlCopyMemory (&LocalObjectAttributes,
418 ObjectAttributes,
419 sizeof(OBJECT_ATTRIBUTES));
420 RtlCreateUnicodeString (&LocalKeyName,
421 ObjectAttributes->ObjectName->Buffer);
422 LocalObjectAttributes.ObjectName = &LocalKeyName;
423 FullNameLength = LocalKeyName.Length;
424
425 /* Remove the last part of the key name and try to create the key again. */
426 while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
427 {
428 Ptr = wcsrchr (LocalKeyName.Buffer, '\\');
429 if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
430 {
431 Status = STATUS_UNSUCCESSFUL;
432 break;
433 }
434 *Ptr = (WCHAR)0;
435 LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
436
437 Status = NtCreateKey (&LocalKeyHandle,
438 KEY_ALL_ACCESS,
439 &LocalObjectAttributes,
440 0,
441 NULL,
442 0,
443 &Disposition);
444 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
445 }
446
447 if (!NT_SUCCESS(Status))
448 {
449 RtlFreeUnicodeString (&LocalKeyName);
450 return Status;
451 }
452
453 /* Add removed parts of the key name and create them too. */
454 while (TRUE)
455 {
456 if (LocalKeyName.Length == FullNameLength)
457 {
458 Status = STATUS_SUCCESS;
459 *KeyHandle = LocalKeyHandle;
460 break;
461 }
462 NtClose (LocalKeyHandle);
463
464 LocalKeyName.Buffer[LocalKeyName.Length / sizeof(WCHAR)] = L'\\';
465 LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
466
467 Status = NtCreateKey (&LocalKeyHandle,
468 KEY_ALL_ACCESS,
469 &LocalObjectAttributes,
470 0,
471 NULL,
472 0,
473 &Disposition);
474 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
475 if (!NT_SUCCESS(Status))
476 break;
477 }
478
479 RtlFreeUnicodeString (&LocalKeyName);
480
481 return Status;
482 }
483 #endif
484
485 /***********************************************************************
486 * registry_callback
487 *
488 * Called once for each AddReg and DelReg entry in a given section.
489 */
490 static BOOLEAN
491 registry_callback (HINF hInf, PCWSTR Section, BOOLEAN Delete)
492 {
493 OBJECT_ATTRIBUTES ObjectAttributes;
494 WCHAR Buffer[MAX_INF_STRING_LENGTH];
495 UNICODE_STRING Name;
496 UNICODE_STRING Value;
497 PUNICODE_STRING ValuePtr;
498 NTSTATUS Status;
499 UINT Flags;
500 ULONG Length;
501
502 INFCONTEXT Context;
503 HANDLE KeyHandle;
504 BOOLEAN Ok;
505
506
507 Ok = SetupFindFirstLineW (hInf, Section, NULL, &Context);
508
509 if (Ok)
510 {
511 for (;Ok; Ok = SetupFindNextLine (&Context, &Context))
512 {
513 /* get root */
514 if (!SetupGetStringFieldW (&Context, 1, Buffer, MAX_INF_STRING_LENGTH, NULL))
515 continue;
516 if (!GetRootKey (Buffer))
517 continue;
518
519 /* get key */
520 Length = wcslen (Buffer);
521 if (!SetupGetStringFieldW (&Context, 2, Buffer + Length, MAX_INF_STRING_LENGTH - Length, NULL))
522 *Buffer = 0;
523
524 DPRINT("KeyName: <%S>\n", Buffer);
525
526 /* get flags */
527 if (!SetupGetIntField (&Context, 4, (PINT)&Flags))
528 Flags = 0;
529
530 DPRINT("Flags: %lx\n", Flags);
531
532 #ifdef __REACTOS__
533 RtlInitUnicodeString (&Name,
534 Buffer);
535
536 InitializeObjectAttributes (&ObjectAttributes,
537 &Name,
538 OBJ_CASE_INSENSITIVE,
539 NULL,
540 NULL);
541
542 if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
543 {
544 Status = NtOpenKey (&KeyHandle,
545 KEY_ALL_ACCESS,
546 &ObjectAttributes);
547 if (!NT_SUCCESS(Status))
548 {
549 DPRINT("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
550 continue; /* ignore if it doesn't exist */
551 }
552 }
553 else
554 {
555 Status = CreateNestedKey (&KeyHandle,
556 KEY_ALL_ACCESS,
557 &ObjectAttributes);
558 if (!NT_SUCCESS(Status))
559 {
560 DPRINT("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
561 continue;
562 }
563 }
564 #else
565 if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
566 {
567 LONG rc = RegOpenKeyW(NULL, Buffer, &KeyHandle);
568 if (rc != ERROR_SUCCESS)
569 {
570 DPRINT("RegOpenKeyW(%S) failed (error %lu)\n", Buffer, rc);
571 continue; /* ignore if it doesn't exist */
572 }
573 }
574 else
575 {
576 LONG rc = RegCreateKeyW(NULL, Buffer, &KeyHandle);
577 if (rc != ERROR_SUCCESS)
578 {
579 DPRINT("RegCreateKeyW(%S) failed (error %lu)\n", Buffer, rc);
580 continue;
581 }
582 }
583 #endif
584
585 /* get value name */
586 if (SetupGetStringFieldW (&Context, 3, Buffer, MAX_INF_STRING_LENGTH, NULL))
587 {
588 RtlInitUnicodeString (&Value,
589 Buffer);
590 ValuePtr = &Value;
591 }
592 else
593 {
594 ValuePtr = NULL;
595 }
596
597 /* and now do it */
598 if (!do_reg_operation (KeyHandle, ValuePtr, &Context, Flags))
599 {
600 NtClose (KeyHandle);
601 return FALSE;
602 }
603
604 #ifdef __REACTOS__
605 NtClose (KeyHandle);
606 #endif
607 }
608 }
609
610 return TRUE;
611 }
612
613
614 BOOLEAN
615 ImportRegistryFile(PWSTR Filename,
616 PWSTR Section,
617 LCID LocaleId,
618 BOOLEAN Delete)
619 {
620 WCHAR FileNameBuffer[MAX_PATH];
621 HINF hInf;
622 UINT ErrorLine;
623
624 /* Load inf file from install media. */
625 wcscpy(FileNameBuffer, SourcePath.Buffer);
626 wcscat(FileNameBuffer, L"\\");
627 wcscat(FileNameBuffer, Filename);
628
629 hInf = SetupOpenInfFileW(
630 FileNameBuffer,
631 NULL,
632 INF_STYLE_WIN4,
633 LocaleId,
634 &ErrorLine);
635 if (hInf == INVALID_HANDLE_VALUE)
636 {
637 DPRINT1("SetupOpenInfFile() failed\n");
638 return FALSE;
639 }
640
641 if (!registry_callback (hInf, L"AddReg", FALSE))
642 {
643 DPRINT1("registry_callback() failed\n");
644 }
645
646 InfCloseFile (hInf);
647
648 return TRUE;
649 }
650
651
652 BOOLEAN
653 SetInstallPathValue(PUNICODE_STRING InstallPath)
654 {
655 OBJECT_ATTRIBUTES ObjectAttributes;
656 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE");
657 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"InstallPath");
658 HANDLE KeyHandle;
659 NTSTATUS Status;
660
661 /* Create the 'secret' InstallPath key */
662 InitializeObjectAttributes (&ObjectAttributes,
663 &KeyName,
664 OBJ_CASE_INSENSITIVE,
665 NULL,
666 NULL);
667 Status = NtOpenKey (&KeyHandle,
668 KEY_ALL_ACCESS,
669 &ObjectAttributes);
670 if (!NT_SUCCESS(Status))
671 {
672 DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
673 return FALSE;
674 }
675
676 Status = NtSetValueKey (KeyHandle,
677 &ValueName,
678 0,
679 REG_SZ,
680 (PVOID)InstallPath->Buffer,
681 InstallPath->Length + sizeof(WCHAR));
682 NtClose(KeyHandle);
683 if (!NT_SUCCESS(Status))
684 {
685 DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
686 return FALSE;
687 }
688
689 return TRUE;
690 }
691
692 BOOLEAN
693 SetMountedDeviceValue(CHAR Letter, ULONG Signature, LARGE_INTEGER StartingOffset)
694 {
695 OBJECT_ATTRIBUTES ObjectAttributes;
696 WCHAR ValueNameBuffer[16];
697 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\MountedDevices");
698 UNICODE_STRING ValueName;
699 REG_DISK_MOUNT_INFO MountInfo;
700 NTSTATUS Status;
701 HANDLE KeyHandle;
702
703 swprintf(ValueNameBuffer, L"\\DosDevices\\%C:", Letter);
704 RtlInitUnicodeString(&ValueName, ValueNameBuffer);
705
706 InitializeObjectAttributes (&ObjectAttributes,
707 &KeyName,
708 OBJ_CASE_INSENSITIVE,
709 NULL,
710 NULL);
711 Status = NtOpenKey (&KeyHandle,
712 KEY_ALL_ACCESS,
713 &ObjectAttributes);
714 if (!NT_SUCCESS(Status))
715 {
716 Status = NtCreateKey(&KeyHandle,
717 KEY_ALL_ACCESS,
718 &ObjectAttributes,
719 0,
720 NULL,
721 REG_OPTION_NON_VOLATILE,
722 NULL);
723 }
724
725 if (!NT_SUCCESS(Status))
726 {
727 DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
728 return FALSE;
729 }
730
731 MountInfo.Signature = Signature;
732 MountInfo.StartingOffset = StartingOffset;
733 Status = NtSetValueKey (KeyHandle,
734 &ValueName,
735 0,
736 REG_BINARY,
737 (PVOID)&MountInfo,
738 sizeof(MountInfo));
739 NtClose(KeyHandle);
740 if (!NT_SUCCESS(Status))
741 {
742 DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
743 return FALSE;
744 }
745
746 return TRUE;
747 }
748
749 /* EOF */