update for HEAD-2003091401
[reactos.git] / ntoskrnl / cm / rtlfunc.c
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * FILE:             ntoskrnl/cm/rtlfunc.c
5  * PURPOSE:          Rtlxxx function for registry access
6  * UPDATE HISTORY:
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <ddk/ntddk.h>
12 #include <roscfg.h>
13 #include <internal/ob.h>
14 #include <limits.h>
15 #include <string.h>
16 #include <internal/registry.h>
17
18 #define NDEBUG
19 #include <internal/debug.h>
20
21 #include "cm.h"
22
23
24 /* FUNCTIONS ****************************************************************/
25
26 /*
27  * @implemented
28  */
29 NTSTATUS STDCALL
30 RtlCheckRegistryKey(IN ULONG RelativeTo,
31                     IN PWSTR Path)
32 {
33   HANDLE KeyHandle;
34   NTSTATUS Status;
35
36   Status = RtlpGetRegistryHandle(RelativeTo,
37                                  Path,
38                                  FALSE,
39                                  &KeyHandle);
40   if (!NT_SUCCESS(Status))
41     return(Status);
42
43   NtClose(KeyHandle);
44
45   return(STATUS_SUCCESS);
46 }
47
48
49 /*
50  * @implemented
51  */
52 NTSTATUS STDCALL
53 RtlCreateRegistryKey(IN ULONG RelativeTo,
54                      IN PWSTR Path)
55 {
56   HANDLE KeyHandle;
57   NTSTATUS Status;
58
59   Status = RtlpGetRegistryHandle(RelativeTo,
60                                  Path,
61                                  TRUE,
62                                  &KeyHandle);
63   if (!NT_SUCCESS(Status))
64     return(Status);
65
66   NtClose(KeyHandle);
67
68   return(STATUS_SUCCESS);
69 }
70
71
72 /*
73  * @implemented
74  */
75 NTSTATUS STDCALL
76 RtlDeleteRegistryValue(IN ULONG RelativeTo,
77                        IN PCWSTR Path,
78                        IN PCWSTR ValueName)
79 {
80   HANDLE KeyHandle;
81   NTSTATUS Status;
82   UNICODE_STRING Name;
83
84   Status = RtlpGetRegistryHandle(RelativeTo,
85                                  (PWSTR) Path,
86                                  TRUE,
87                                  &KeyHandle);
88   if (!NT_SUCCESS(Status))
89     return(Status);
90
91   RtlInitUnicodeString(&Name,
92                        ValueName);
93
94   NtDeleteValueKey(KeyHandle,
95                    &Name);
96
97   NtClose(KeyHandle);
98
99   return(STATUS_SUCCESS);
100 }
101
102
103 NTSTATUS STDCALL
104 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,
105                    OUT PHANDLE KeyHandle)
106 {
107   OBJECT_ATTRIBUTES ObjectAttributes;
108   UNICODE_STRING KeyPath = UNICODE_STRING_INITIALIZER(L"\\Registry\\User\\.Default");
109   NTSTATUS Status;
110
111   Status = RtlFormatCurrentUserKeyPath(&KeyPath);
112   if (NT_SUCCESS(Status))
113     {
114       InitializeObjectAttributes(&ObjectAttributes,
115                                  &KeyPath,
116                                  OBJ_CASE_INSENSITIVE,
117                                  NULL,
118                                  NULL);
119       Status = NtOpenKey(KeyHandle,
120                          DesiredAccess,
121                          &ObjectAttributes);
122       RtlFreeUnicodeString(&KeyPath);
123       if (NT_SUCCESS(Status))
124         return(STATUS_SUCCESS);
125     }
126
127   InitializeObjectAttributes(&ObjectAttributes,
128                              &KeyPath,
129                              OBJ_CASE_INSENSITIVE,
130                              NULL,
131                              NULL);
132   Status = NtOpenKey(KeyHandle,
133                      DesiredAccess,
134                      &ObjectAttributes);
135   return(Status);
136 }
137
138
139 /*
140  * @unimplemented
141  */
142 NTSTATUS STDCALL
143 RtlQueryRegistryValues(IN ULONG RelativeTo,
144                        IN PCWSTR Path,
145                        IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
146                        IN PVOID Context,
147                        IN PVOID Environment)
148 {
149   NTSTATUS Status;
150   HANDLE BaseKeyHandle;
151   HANDLE CurrentKeyHandle;
152   PRTL_QUERY_REGISTRY_TABLE QueryEntry;
153   OBJECT_ATTRIBUTES ObjectAttributes;
154   UNICODE_STRING KeyName;
155   PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
156   PKEY_VALUE_FULL_INFORMATION FullValueInfo;
157   ULONG BufferSize;
158   ULONG ResultSize;
159   ULONG ValueNameSize;
160   ULONG Index;
161   ULONG StringLen;
162   PWSTR StringPtr;
163   PWSTR ValueName;
164
165   DPRINT("RtlQueryRegistryValues() called\n");
166
167   Status = RtlpGetRegistryHandle(RelativeTo,
168                                  (PWSTR) Path,
169                                  FALSE,
170                                  &BaseKeyHandle);
171   if (!NT_SUCCESS(Status))
172     {
173       DPRINT("RtlpGetRegistryHandle() failed with status %x\n", Status);
174       return(Status);
175     }
176
177   CurrentKeyHandle = BaseKeyHandle;
178   QueryEntry = QueryTable;
179   while ((QueryEntry->QueryRoutine != NULL) ||
180          (QueryEntry->Name != NULL))
181     {
182       if (((QueryEntry->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_TOPKEY)) != 0) &&
183           (BaseKeyHandle != CurrentKeyHandle))
184         {
185           NtClose(CurrentKeyHandle);
186           CurrentKeyHandle = BaseKeyHandle;
187         }
188
189       if (QueryEntry->Flags & RTL_QUERY_REGISTRY_SUBKEY)
190         {
191           DPRINT("Open new subkey: %S\n", QueryEntry->Name);
192
193           RtlInitUnicodeString(&KeyName,
194                                QueryEntry->Name);
195           InitializeObjectAttributes(&ObjectAttributes,
196                                      &KeyName,
197                                      OBJ_CASE_INSENSITIVE,
198                                      BaseKeyHandle,
199                                      NULL);
200           Status = NtOpenKey(&CurrentKeyHandle,
201                              KEY_ALL_ACCESS,
202                              &ObjectAttributes);
203           if (!NT_SUCCESS(Status))
204             break;
205         }
206       else if (QueryEntry->Flags & RTL_QUERY_REGISTRY_DIRECT)
207         {
208           DPRINT("Query value directly: %S\n", QueryEntry->Name);
209
210           RtlInitUnicodeString(&KeyName,
211                                QueryEntry->Name);
212
213           BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
214           ValueInfo = ExAllocatePool(PagedPool, BufferSize);
215           if (ValueInfo == NULL)
216             {
217               Status = STATUS_NO_MEMORY;
218               break;
219             }
220
221           Status = ZwQueryValueKey(CurrentKeyHandle,
222                                    &KeyName,
223                                    KeyValuePartialInformation,
224                                    ValueInfo,
225                                    BufferSize,
226                                    &ResultSize);
227           if (!NT_SUCCESS(Status))
228             {
229               if (QueryEntry->Flags & RTL_QUERY_REGISTRY_REQUIRED)
230                 {
231                   ExFreePool(ValueInfo);
232                   Status = STATUS_OBJECT_NAME_NOT_FOUND;
233                   goto ByeBye;
234                 }
235         
236               if (QueryEntry->DefaultType == REG_SZ)
237                 {
238                   PUNICODE_STRING ValueString;
239                   PUNICODE_STRING SourceString;
240
241                   SourceString = (PUNICODE_STRING)QueryEntry->DefaultData;
242                   ValueString = (PUNICODE_STRING)QueryEntry->EntryContext;
243                   if (ValueString->Buffer == 0)
244                     {
245                       ValueString->Length = SourceString->Length;
246                       ValueString->MaximumLength = SourceString->MaximumLength;
247                       ValueString->Buffer = ExAllocatePool(PagedPool,
248                                                            ValueString->MaximumLength);
249                       if (!ValueString->Buffer)
250                         break;
251                       ValueString->Buffer[0] = 0;
252                       memcpy(ValueString->Buffer,
253                              SourceString->Buffer,
254                              SourceString->MaximumLength);
255                     }
256                   else
257                     {
258                       ValueString->Length = RtlMin(SourceString->Length,
259                                                    ValueString->MaximumLength - sizeof(WCHAR));
260                       memcpy(ValueString->Buffer,
261                              SourceString->Buffer,
262                              ValueString->Length);
263                       ((PWSTR)ValueString->Buffer)[ValueString->Length / sizeof(WCHAR)] = 0;
264                     }
265                 }
266               else
267                 {
268                   memcpy(QueryEntry->EntryContext,
269                          QueryEntry->DefaultData,
270                          QueryEntry->DefaultLength);
271                 }
272               Status = STATUS_SUCCESS;
273             }
274           else
275             {
276               if (ValueInfo->Type == REG_SZ ||
277                   ValueInfo->Type == REG_MULTI_SZ ||
278                   ValueInfo->Type == REG_EXPAND_SZ)
279                 {
280                   PUNICODE_STRING ValueString;
281
282                   ValueString = (PUNICODE_STRING)QueryEntry->EntryContext;
283                   if (ValueString->Buffer == 0)
284                     {
285                       RtlInitUnicodeString(ValueString,
286                                            NULL);
287                       ValueString->MaximumLength = ValueInfo->DataLength;
288                       ValueString->Buffer = ExAllocatePool(PagedPool,
289                                                            ValueString->MaximumLength);
290                       if (!ValueString->Buffer)
291                         break;
292                       ValueString->Buffer[0] = 0;
293                     }
294                   ValueString->Length = RtlMin(ValueInfo->DataLength,
295                                                ValueString->MaximumLength) - sizeof(WCHAR);
296                   memcpy(ValueString->Buffer,
297                          ValueInfo->Data,
298                          ValueString->Length);
299                   ((PWSTR)ValueString->Buffer)[ValueString->Length / sizeof(WCHAR)] = 0;
300                 }
301               else
302                 {
303                   memcpy(QueryEntry->EntryContext,
304                          ValueInfo->Data,
305                          ValueInfo->DataLength);
306                 }
307             }
308
309           if (QueryEntry->Flags & RTL_QUERY_REGISTRY_DELETE)
310             {
311               DPRINT("FIXME: Delete value: %S\n", QueryEntry->Name);
312
313             }
314
315           ExFreePool(ValueInfo);
316         }
317       else
318         {
319           DPRINT("Query value via query routine: %S\n", QueryEntry->Name);
320
321           if (QueryEntry->Name != NULL)
322             {
323               DPRINT("Callback\n");
324
325               RtlInitUnicodeString(&KeyName,
326                                    QueryEntry->Name);
327
328               BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
329               ValueInfo = ExAllocatePool(PagedPool,
330                                          BufferSize);
331               if (ValueInfo == NULL)
332                 {
333                   Status = STATUS_NO_MEMORY;
334                   break;
335                 }
336
337               Status = NtQueryValueKey(CurrentKeyHandle,
338                                        &KeyName,
339                                        KeyValuePartialInformation,
340                                        ValueInfo,
341                                        BufferSize,
342                                        &ResultSize);
343               if (!NT_SUCCESS(Status))
344                 {
345                   Status = QueryEntry->QueryRoutine(QueryEntry->Name,
346                                                     QueryEntry->DefaultType,
347                                                     QueryEntry->DefaultData,
348                                                     QueryEntry->DefaultLength,
349                                                     Context,
350                                                     QueryEntry->EntryContext);
351                 }
352               else if ((ValueInfo->Type == REG_MULTI_SZ) &&
353                        !(QueryEntry->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
354                 {
355                   DPRINT("Expand REG_MULTI_SZ type\n");
356                   StringPtr = (PWSTR)ValueInfo->Data;
357                   while (*StringPtr != 0)
358                     {
359                       StringLen = (wcslen(StringPtr) + 1) * sizeof(WCHAR);
360                       Status = QueryEntry->QueryRoutine(QueryEntry->Name,
361                                                         REG_SZ,
362                                                         (PVOID)StringPtr,
363                                                         StringLen,
364                                                         Context,
365                                                         QueryEntry->EntryContext);
366                       if(!NT_SUCCESS(Status))
367                         break;
368                       StringPtr = (PWSTR)((PUCHAR)StringPtr + StringLen);
369                     }
370                 }
371               else
372                 {
373                   Status = QueryEntry->QueryRoutine(QueryEntry->Name,
374                                                     ValueInfo->Type,
375                                                     ValueInfo->Data,
376                                                     ValueInfo->DataLength,
377                                                     Context,
378                                                     QueryEntry->EntryContext);
379                 }
380
381               if (QueryEntry->Flags & RTL_QUERY_REGISTRY_DELETE)
382                 {
383                   DPRINT("FIXME: Delete value: %S\n", QueryEntry->Name);
384
385                 }
386
387               ExFreePool(ValueInfo);
388
389               if (!NT_SUCCESS(Status))
390                 break;
391             }
392           else if (QueryEntry->Flags & RTL_QUERY_REGISTRY_NOVALUE)
393             {
394               DPRINT("Simple callback\n");
395               Status = QueryEntry->QueryRoutine(NULL,
396                                                 REG_NONE,
397                                                 NULL,
398                                                 0,
399                                                 Context,
400                                                 QueryEntry->EntryContext);
401               if (!NT_SUCCESS(Status))
402                 break;
403             }
404           else
405             {
406               DPRINT("Enumerate values\n");
407
408               BufferSize = sizeof(KEY_VALUE_FULL_INFORMATION) + 4096;
409               FullValueInfo = ExAllocatePool(PagedPool,
410                                              BufferSize);
411               if (FullValueInfo == NULL)
412                 {
413                   Status = STATUS_NO_MEMORY;
414                   break;
415                 }
416               ValueNameSize = 256 * sizeof(WCHAR);
417               ValueName = ExAllocatePool(PagedPool,
418                                          ValueNameSize);
419               if (ValueName == NULL)
420                 {
421                   Status = STATUS_NO_MEMORY;
422                   break;
423                 }
424               Index = 0;
425               while (TRUE)
426                 {
427                   Status = NtEnumerateValueKey(CurrentKeyHandle,
428                                                Index,
429                                                KeyValueFullInformation,
430                                                FullValueInfo,
431                                                BufferSize,
432                                                &ResultSize);
433                   if (!NT_SUCCESS(Status))
434                     {
435                       if ((Status == STATUS_NO_MORE_ENTRIES) &&
436                           (Index == 0) &&
437                           (QueryEntry->Flags & RTL_QUERY_REGISTRY_REQUIRED))
438                         {
439                           Status = STATUS_OBJECT_NAME_NOT_FOUND;
440                         }
441                       else if (Status == STATUS_NO_MORE_ENTRIES)
442                         {
443                           Status = STATUS_SUCCESS;
444                         }
445                       break;
446                     }
447
448                   if (FullValueInfo->NameLength > ValueNameSize - sizeof(WCHAR))
449                     {
450                       /* Should not happen, because the name length is limited to 255 characters */
451                       ExFreePool(ValueName);
452                       ValueNameSize = FullValueInfo->NameLength + sizeof(WCHAR);
453                       ValueName = ExAllocatePool(PagedPool, ValueNameSize);
454                       if (ValueName == NULL)
455                         {
456                           Status = STATUS_NO_MEMORY;
457                           break;
458                         }
459                     }
460
461                   RtlCopyMemory(ValueName, 
462                                 FullValueInfo->Name,
463                                 FullValueInfo->NameLength);
464                   ValueName[FullValueInfo->NameLength / sizeof(WCHAR)] = 0;
465
466                   if ((FullValueInfo->Type == REG_MULTI_SZ) &&
467                       !(QueryEntry->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
468                     {
469                       DPRINT("Expand REG_MULTI_SZ type\n");
470
471                       StringPtr = (PWSTR)((PVOID)FullValueInfo + FullValueInfo->DataOffset);
472                       while (*StringPtr != 0)
473                         {
474                           StringLen = (wcslen(StringPtr) + 1) * sizeof(WCHAR);
475                           Status = QueryEntry->QueryRoutine(ValueName,
476                                                             REG_SZ,
477                                                             (PVOID)StringPtr,
478                                                             StringLen,
479                                                             Context,
480                                                             QueryEntry->EntryContext);
481                           if(!NT_SUCCESS(Status))
482                             break;
483                           StringPtr = (PWSTR)((PUCHAR)StringPtr + StringLen);
484                         }
485                     }
486                   else
487                     {
488                       Status = QueryEntry->QueryRoutine(ValueName,
489                                                         FullValueInfo->Type,
490                                                         (PVOID)FullValueInfo + FullValueInfo->DataOffset,
491                                                         FullValueInfo->DataLength,
492                                                         Context,
493                                                         QueryEntry->EntryContext);
494                     }
495
496                   if (!NT_SUCCESS(Status))
497                     break;
498
499                   /* FIXME: How will these be deleted? */
500
501                   Index++;
502                 }
503
504               ExFreePool(FullValueInfo);
505               ExFreePool(ValueName);
506
507               if (!NT_SUCCESS(Status))
508                 break;
509             }
510         }
511
512       QueryEntry++;
513     }
514
515 ByeBye:
516
517   if (CurrentKeyHandle != BaseKeyHandle)
518     NtClose(CurrentKeyHandle);
519
520   NtClose(BaseKeyHandle);
521
522   return Status;
523 }
524
525
526 /*
527  * @implemented
528  */
529 NTSTATUS STDCALL
530 RtlWriteRegistryValue(IN ULONG RelativeTo,
531                       IN PCWSTR Path,
532                       IN PCWSTR ValueName,
533                       IN ULONG ValueType,
534                       IN PVOID ValueData,
535                       IN ULONG ValueLength)
536 {
537   HANDLE KeyHandle;
538   NTSTATUS Status;
539   UNICODE_STRING Name;
540
541   Status = RtlpGetRegistryHandle(RelativeTo,
542                                  (PWSTR) Path,
543                                  TRUE,
544                                  &KeyHandle);
545   if (!NT_SUCCESS(Status))
546     return(Status);
547
548   RtlInitUnicodeString(&Name,
549                        ValueName);
550
551   NtSetValueKey(KeyHandle,
552                 &Name,
553                 0,
554                 ValueType,
555                 ValueData,
556                 ValueLength);
557
558   NtClose(KeyHandle);
559
560   return(STATUS_SUCCESS);
561 }
562
563
564 /*
565  * @unimplemented
566  */
567 NTSTATUS STDCALL
568 RtlFormatCurrentUserKeyPath(IN OUT PUNICODE_STRING KeyPath)
569 {
570   /* FIXME: !!! */
571   RtlCreateUnicodeString(KeyPath,
572                          L"\\Registry\\User\\.Default");
573
574   return(STATUS_SUCCESS);
575 }
576
577 /*  ------------------------------------------  Private Implementation  */
578
579
580 NTSTATUS
581 RtlpGetRegistryHandle(ULONG RelativeTo,
582                       PWSTR Path,
583                       BOOLEAN Create,
584                       PHANDLE KeyHandle)
585 {
586   UNICODE_STRING KeyName;
587   WCHAR KeyBuffer[MAX_PATH];
588   OBJECT_ATTRIBUTES ObjectAttributes;
589   NTSTATUS Status;
590
591   if (RelativeTo & RTL_REGISTRY_HANDLE)
592     {
593       Status = NtDuplicateObject(PsGetCurrentProcessId(),
594                                  (HANDLE)Path,
595                                  PsGetCurrentProcessId(),
596                                  KeyHandle,
597                                  0,
598                                  FALSE,
599                                  DUPLICATE_SAME_ACCESS);
600       return(Status);
601     }
602
603   if (RelativeTo & RTL_REGISTRY_OPTIONAL)
604     RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
605
606   if (RelativeTo >= RTL_REGISTRY_MAXIMUM)
607     return STATUS_INVALID_PARAMETER;
608
609   KeyName.Length = 0;
610   KeyName.MaximumLength = MAX_PATH;
611   KeyName.Buffer = KeyBuffer;
612   KeyBuffer[0] = 0;
613
614   switch (RelativeTo)
615     {
616       case RTL_REGISTRY_SERVICES:
617         RtlAppendUnicodeToString(&KeyName,
618                                  L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
619         break;
620
621       case RTL_REGISTRY_CONTROL:
622         RtlAppendUnicodeToString(&KeyName,
623                                  L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\");
624         break;
625
626       case RTL_REGISTRY_WINDOWS_NT:
627         RtlAppendUnicodeToString(&KeyName,
628                                  L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\");
629         break;
630
631       case RTL_REGISTRY_DEVICEMAP:
632         RtlAppendUnicodeToString(&KeyName,
633                                  L"\\Registry\\Machine\\Hardware\\DeviceMap\\");
634         break;
635
636       case RTL_REGISTRY_USER:
637         Status = RtlFormatCurrentUserKeyPath(&KeyName);
638         if (!NT_SUCCESS(Status))
639           return(Status);
640         break;
641
642       /* ReactOS specific */
643       case RTL_REGISTRY_ENUM:
644         RtlAppendUnicodeToString(&KeyName,
645                                  L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
646         break;
647     }
648
649   if (Path[0] == L'\\' && RelativeTo != RTL_REGISTRY_ABSOLUTE)
650     {
651       Path++;
652     }
653   RtlAppendUnicodeToString(&KeyName,
654                            Path);
655
656   DPRINT("KeyName '%wZ'\n", &KeyName);
657
658   InitializeObjectAttributes(&ObjectAttributes,
659                              &KeyName,
660                              OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
661                              NULL,
662                              NULL);
663
664   if (Create == TRUE)
665     {
666       Status = NtCreateKey(KeyHandle,
667                            KEY_ALL_ACCESS,
668                            &ObjectAttributes,
669                            0,
670                            NULL,
671                            0,
672                            NULL);
673     }
674   else
675     {
676       Status = NtOpenKey(KeyHandle,
677                          KEY_ALL_ACCESS,
678                          &ObjectAttributes);
679     }
680
681   return(Status);
682 }
683
684
685 NTSTATUS
686 RtlpCreateRegistryKeyPath(PWSTR Path)
687 {
688   OBJECT_ATTRIBUTES ObjectAttributes;
689   WCHAR KeyBuffer[MAX_PATH];
690   UNICODE_STRING KeyName;
691   HANDLE KeyHandle;
692   NTSTATUS Status;
693   PWCHAR Current;
694   PWCHAR Next;
695
696   if (_wcsnicmp(Path, L"\\Registry\\", 10) != 0)
697     {
698       return STATUS_INVALID_PARAMETER;
699     }
700
701   wcsncpy (KeyBuffer, Path, MAX_PATH-1);
702   RtlInitUnicodeString (&KeyName, KeyBuffer);
703
704   /* Skip \\Registry\\ */
705   Current = KeyName.Buffer;
706   Current = wcschr (Current, '\\') + 1;
707   Current = wcschr (Current, '\\') + 1;
708
709   do
710    {
711       Next = wcschr (Current, '\\');
712       if (Next == NULL)
713         {
714           /* The end */
715         }
716       else
717         {
718           *Next = 0;
719         }
720
721       InitializeObjectAttributes (&ObjectAttributes,
722                                   &KeyName,
723                                   OBJ_CASE_INSENSITIVE,
724                                   NULL,
725                                   NULL);
726
727       DPRINT("Create '%S'\n", KeyName.Buffer);
728
729       Status = NtCreateKey (&KeyHandle,
730                             KEY_ALL_ACCESS,
731                             &ObjectAttributes,
732                             0,
733                             NULL,
734                             0,
735                             NULL);
736       if (!NT_SUCCESS (Status))
737         {
738           DPRINT ("NtCreateKey() failed with status %x\n", Status);
739           return Status;
740         }
741
742       NtClose (KeyHandle);
743
744       if (Next != NULL)
745         {
746           *Next = L'\\';
747         }
748
749       Current = Next + 1;
750     }
751    while (Next != NULL);
752
753   return STATUS_SUCCESS;
754 }
755
756 /* EOF */