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