xref: /reactos/sdk/lib/rtl/registry.c (revision 02883d1c)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS system libraries
4  * PURPOSE:           Rtl registry functions
5  * FILE:              lib/rtl/registry.c
6  * PROGRAMER:         Alex Ionescu (alex.ionescu@reactos.org)
7  *                    Eric Kohl
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <rtl.h>
13 
14 #include <ndk/cmfuncs.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 #define TAG_RTLREGISTRY 'vrqR'
20 
21 extern SIZE_T RtlpAllocDeallocQueryBufferSize;
22 
23 /* DATA **********************************************************************/
24 
25 PCWSTR RtlpRegPaths[RTL_REGISTRY_MAXIMUM] =
26 {
27     NULL,
28     L"\\Registry\\Machine\\System\\CurrentControlSet\\Services",
29     L"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
30     L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
31     L"\\Registry\\Machine\\Hardware\\DeviceMap",
32     L"\\Registry\\User\\.Default",
33 };
34 
35 /* PRIVATE FUNCTIONS *********************************************************/
36 
37 NTSTATUS
38 NTAPI
RtlpQueryRegistryDirect(IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Buffer)39 RtlpQueryRegistryDirect(IN ULONG ValueType,
40                         IN PVOID ValueData,
41                         IN ULONG ValueLength,
42                         IN PVOID Buffer)
43 {
44     USHORT ActualLength;
45     PUNICODE_STRING ReturnString = Buffer;
46     PULONG Length = Buffer;
47     ULONG RealLength;
48 
49     /* Check if this is a string */
50     if ((ValueType == REG_SZ) ||
51         (ValueType == REG_EXPAND_SZ) ||
52         (ValueType == REG_MULTI_SZ))
53     {
54         /* Normalize the length */
55         if (ValueLength > MAXUSHORT)
56             ActualLength = MAXUSHORT;
57         else
58             ActualLength = (USHORT)ValueLength;
59 
60         /* Check if the return string has been allocated */
61         if (!ReturnString->Buffer)
62         {
63             /* Allocate it */
64             ReturnString->Buffer = RtlpAllocateStringMemory(ActualLength, TAG_RTLREGISTRY);
65             if (!ReturnString->Buffer) return STATUS_NO_MEMORY;
66             ReturnString->MaximumLength = ActualLength;
67         }
68         else if (ActualLength > ReturnString->MaximumLength)
69         {
70             /* The string the caller allocated is too small */
71             return STATUS_BUFFER_TOO_SMALL;
72         }
73 
74         /* Copy the data */
75         RtlCopyMemory(ReturnString->Buffer, ValueData, ActualLength);
76         ReturnString->Length = ActualLength - sizeof(UNICODE_NULL);
77     }
78     else if (ValueLength <= sizeof(ULONG))
79     {
80         /* Check if we can just copy the data */
81         if ((Buffer != ValueData) && (ValueLength))
82         {
83             /* Copy it */
84             RtlCopyMemory(Buffer, ValueData, ValueLength);
85         }
86     }
87     else
88     {
89         /* Check if the length is negative */
90         if ((LONG)*Length < 0)
91         {
92             /* Get the real length and copy the buffer */
93             RealLength = -(LONG)*Length;
94             if (RealLength < ValueLength) return STATUS_BUFFER_TOO_SMALL;
95             RtlCopyMemory(Buffer, ValueData, ValueLength);
96         }
97         else
98         {
99             if (ValueType != REG_BINARY)
100             {
101                 /* Check if there's space for the length and type, plus data */
102                 if (*Length < (2 * sizeof(ULONG) + ValueLength))
103                 {
104                     /* Nope, fail */
105                     return STATUS_BUFFER_TOO_SMALL;
106                 }
107 
108                 /* Return the data */
109                 *Length++ = ValueLength;
110                 *Length++ = ValueType;
111                 RtlCopyMemory(Length, ValueData, ValueLength);
112             }
113             else
114             {
115                 /* Return the REG_BINARY data */
116                 RtlCopyMemory(Length, ValueData, ValueLength);
117             }
118         }
119     }
120 
121     /* All done */
122     return STATUS_SUCCESS;
123 }
124 
125 NTSTATUS
126 NTAPI
RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable,IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo,IN OUT PULONG InfoSize,IN PVOID Context,IN PVOID Environment)127 RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
128                              IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo,
129                              IN OUT PULONG InfoSize,
130                              IN PVOID Context,
131                              IN PVOID Environment)
132 {
133     ULONG InfoLength;
134     SIZE_T Length, SpareLength, c;
135     ULONG RequiredLength;
136     PCHAR SpareData, DataEnd;
137     ULONG Type;
138     PWCHAR Name, p, ValueEnd;
139     PVOID Data;
140     NTSTATUS Status;
141     BOOLEAN FoundExpander = FALSE;
142     UNICODE_STRING Source, Destination;
143 
144     /* Setup defaults */
145     InfoLength = *InfoSize;
146     *InfoSize = 0;
147 
148     /* Check if there's no data */
149     if (KeyValueInfo->DataOffset == MAXULONG)
150     {
151         /* Return proper status code */
152         return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ?
153                 STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS;
154     }
155 
156     /* Setup spare data pointers */
157     SpareData = (PCHAR)KeyValueInfo;
158     SpareLength = InfoLength;
159     DataEnd = SpareData + SpareLength;
160 
161     /* Check if there's no value or data */
162     if ((KeyValueInfo->Type == REG_NONE) ||
163         (!(KeyValueInfo->DataLength) &&
164           (KeyValueInfo->Type == QueryTable->DefaultType)))
165     {
166         /* Check if there's no value */
167         if (QueryTable->DefaultType == REG_NONE)
168         {
169             /* Return proper status code */
170             return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ?
171                     STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS;
172         }
173 
174         /* We can setup a default value... capture the defaults */
175         Name = (PWCHAR)QueryTable->Name;
176         Type = QueryTable->DefaultType;
177         Data = QueryTable->DefaultData;
178         Length = QueryTable->DefaultLength;
179         if (!Length)
180         {
181             /* No default length given, try to calculate it */
182             p = Data;
183             if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
184             {
185                 /* This is a string, count the characters */
186                 while (*p++);
187                 Length = (ULONG_PTR)p - (ULONG_PTR)Data;
188             }
189             else if (Type == REG_MULTI_SZ)
190             {
191                 /* This is a multi-string, calculate all characters */
192                 while (*p) while (*p++);
193                 Length = (ULONG_PTR)p - (ULONG_PTR)Data + sizeof(UNICODE_NULL);
194             }
195         }
196     }
197     else
198     {
199         /* Check if we have length */
200         if (KeyValueInfo->DataLength)
201         {
202             /* Increase the spare data */
203             SpareData += KeyValueInfo->DataOffset +
204                          KeyValueInfo->DataLength;
205         }
206         else
207         {
208             /* Otherwise, the spare data only has the name data */
209             SpareData += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
210                          KeyValueInfo->NameLength;
211         }
212 
213         /* Align the pointer and get new size of spare data */
214         SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
215         SpareLength = DataEnd - SpareData;
216 
217         /* Check if we have space to copy the data */
218         RequiredLength = KeyValueInfo->NameLength + sizeof(UNICODE_NULL);
219         if ((SpareData > DataEnd) || (SpareLength < RequiredLength))
220         {
221             /* Fail and return the missing length */
222             *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + RequiredLength;
223             return STATUS_BUFFER_TOO_SMALL;
224         }
225 
226         /* Check if this isn't a direct return */
227         if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT))
228         {
229             /* Copy the data and null-terminate it */
230             Name = (PWCHAR)SpareData;
231             RtlCopyMemory(Name, KeyValueInfo->Name, KeyValueInfo->NameLength);
232             Name[KeyValueInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
233 
234             /* Update the spare data information */
235             SpareData += RequiredLength;
236             SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
237             SpareLength = DataEnd - SpareData;
238         }
239         else
240         {
241             /* Just return the name */
242             Name = (PWCHAR)QueryTable->Name;
243         }
244 
245         /* Capture key data */
246         Type = KeyValueInfo->Type;
247         Data = (PVOID)((ULONG_PTR)KeyValueInfo + KeyValueInfo->DataOffset);
248         Length = KeyValueInfo->DataLength;
249     }
250 
251     /* Check if we're expanding */
252     if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
253     {
254         /* Check if it's a multi-string */
255         if (Type == REG_MULTI_SZ)
256         {
257             /* Prepare defaults */
258             Status = STATUS_SUCCESS;
259             /* Skip the last two UNICODE_NULL chars (the terminating null string) */
260             ValueEnd = (PWSTR)((ULONG_PTR)Data + Length - 2 * sizeof(UNICODE_NULL));
261             p = Data;
262 
263             /* Loop all strings */
264             while (p < ValueEnd)
265             {
266                 /* Go to the next string */
267                 while (*p++);
268 
269                 /* Get the length and check if this is direct */
270                 Length = (ULONG_PTR)p - (ULONG_PTR)Data;
271                 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)
272                 {
273                     /* Do the query */
274                     Status = RtlpQueryRegistryDirect(REG_SZ,
275                                                      Data,
276                                                      (ULONG)Length,
277                                                      QueryTable->EntryContext);
278                     QueryTable->EntryContext =
279                         (PVOID)((ULONG_PTR)QueryTable->EntryContext +
280                                 sizeof(UNICODE_STRING));
281                 }
282                 else
283                 {
284                     /* Call the custom routine */
285                     Status = QueryTable->QueryRoutine(Name,
286                                                       REG_SZ,
287                                                       Data,
288                                                       (ULONG)Length,
289                                                       Context,
290                                                       QueryTable->EntryContext);
291                 }
292 
293                 /* Normalize status */
294                 if (Status == STATUS_BUFFER_TOO_SMALL) Status = STATUS_SUCCESS;
295                 if (!NT_SUCCESS(Status)) break;
296 
297                 /* Update data pointer */
298                 Data = p;
299             }
300 
301             /* Return */
302             return Status;
303         }
304 
305         /* Check if this is an expand string */
306         if ((Type == REG_EXPAND_SZ) && (Length >= sizeof(WCHAR)))
307         {
308             /* Try to find the expander */
309             c = Length - sizeof(UNICODE_NULL);
310             p = (PWCHAR)Data;
311             while (c)
312             {
313                 /* Check if this is one */
314                 if (*p == L'%')
315                 {
316                     /* Yup! */
317                     FoundExpander = TRUE;
318                     break;
319                 }
320 
321                 /* Continue in the buffer */
322                 p++;
323                 c -= sizeof(WCHAR);
324             }
325 
326             /* So check if we have one */
327             if (FoundExpander)
328             {
329                 /* Setup the source string */
330                 RtlInitEmptyUnicodeString(&Source, Data, (USHORT)Length);
331                 Source.Length = Source.MaximumLength - sizeof(UNICODE_NULL);
332 
333                 /* Setup the destination string */
334                 RtlInitEmptyUnicodeString(&Destination, (PWCHAR)SpareData, 0);
335 
336                 /* Check if we're out of space */
337                 if (SpareLength <= 0)
338                 {
339                     /* Then we don't have any space in our string */
340                     Destination.MaximumLength = 0;
341                 }
342                 else if (SpareLength <= MAXUSHORT)
343                 {
344                     /* This is the good case, where we fit into a string */
345                     Destination.MaximumLength = (USHORT)SpareLength;
346                     Destination.Buffer[SpareLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
347                 }
348                 else
349                 {
350                     /* We can't fit into a string, so truncate */
351                     Destination.MaximumLength = MAXUSHORT;
352                     Destination.Buffer[MAXUSHORT / sizeof(WCHAR) - 1] = UNICODE_NULL;
353                 }
354 
355                 /* Expand the strings and set our type as one string */
356                 Status = RtlExpandEnvironmentStrings_U(Environment,
357                                                        &Source,
358                                                        &Destination,
359                                                        &RequiredLength);
360                 Type = REG_SZ;
361 
362                 /* Check for success */
363                 if (NT_SUCCESS(Status))
364                 {
365                     /* Set the value name and length to our string */
366                     Data = Destination.Buffer;
367                     Length = Destination.Length + sizeof(UNICODE_NULL);
368                 }
369                 else
370                 {
371                     /* Check if our buffer is too small */
372                     if (Status == STATUS_BUFFER_TOO_SMALL)
373                     {
374                         /* Set the required missing length */
375                         *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) +
376                                            RequiredLength;
377 
378                         /* Notify debugger */
379                         DPRINT1("RTL: Expand variables for %wZ failed - "
380                                 "Status == %lx Size %x > %x <%x>\n",
381                                 &Source,
382                                 Status,
383                                 *InfoSize,
384                                 InfoLength,
385                                 Destination.MaximumLength);
386                     }
387                     else
388                     {
389                         /* Notify debugger */
390                         DPRINT1("RTL: Expand variables for %wZ failed - "
391                                 "Status == %lx\n",
392                                 &Source,
393                                 Status);
394                     }
395 
396                     /* Return the status */
397                     return Status;
398                 }
399             }
400         }
401     }
402 
403     /* Check if this is a direct query */
404     if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)
405     {
406         /* Return the data */
407         Status = RtlpQueryRegistryDirect(Type,
408                                          Data,
409                                          (ULONG)Length,
410                                          QueryTable->EntryContext);
411     }
412     else
413     {
414         /* Call the query routine */
415         Status = QueryTable->QueryRoutine(Name,
416                                           Type,
417                                           Data,
418                                           (ULONG)Length,
419                                           Context,
420                                           QueryTable->EntryContext);
421     }
422 
423     /* Normalize and return status */
424     return (Status == STATUS_BUFFER_TOO_SMALL) ? STATUS_SUCCESS : Status;
425 }
426 
427 _Success_(return!=NULL || BufferSize==0)
428 _When_(BufferSize!=NULL,__drv_allocatesMem(Mem))
429 PVOID
430 NTAPI
431 RtlpAllocDeallocQueryBuffer(
432     _In_opt_ PSIZE_T BufferSize,
433     _In_opt_ __drv_freesMem(Mem) PVOID OldBuffer,
434     _In_ SIZE_T OldBufferSize,
435     _Out_opt_ _On_failure_(_Post_satisfies_(*Status < 0)) PNTSTATUS Status)
436 {
437     PVOID Buffer = NULL;
438 
439     /* Assume success */
440     if (Status) *Status = STATUS_SUCCESS;
441 
442     /* Free the old buffer */
443     if (OldBuffer) RtlpFreeMemory(OldBuffer, TAG_RTLREGISTRY);
444 
445     /* Check if we need to allocate a new one */
446     if (BufferSize)
447     {
448         /* Allocate */
449         Buffer = RtlpAllocateMemory(*BufferSize, TAG_RTLREGISTRY);
450         if (!(Buffer) && (Status)) *Status = STATUS_NO_MEMORY;
451     }
452 
453     /* Return the pointer */
454     return Buffer;
455 }
456 
457 NTSTATUS
458 NTAPI
RtlpGetRegistryHandle(IN ULONG RelativeTo,IN PCWSTR Path,IN BOOLEAN Create,IN PHANDLE KeyHandle)459 RtlpGetRegistryHandle(IN ULONG RelativeTo,
460                       IN PCWSTR Path,
461                       IN BOOLEAN Create,
462                       IN PHANDLE KeyHandle)
463 {
464     UNICODE_STRING KeyPath, KeyName;
465     WCHAR KeyBuffer[MAX_PATH];
466     OBJECT_ATTRIBUTES ObjectAttributes;
467     NTSTATUS Status;
468 
469     /* Check if we just want the handle */
470     if (RelativeTo & RTL_REGISTRY_HANDLE)
471     {
472         *KeyHandle = (HANDLE)Path;
473         return STATUS_SUCCESS;
474     }
475 
476     /* Check for optional flag */
477     if (RelativeTo & RTL_REGISTRY_OPTIONAL)
478     {
479         /* Mask it out */
480         RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
481     }
482 
483     /* Fail on invalid parameter */
484     if (RelativeTo >= RTL_REGISTRY_MAXIMUM) return STATUS_INVALID_PARAMETER;
485 
486     /* Initialize the key name */
487     RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer));
488 
489     /* Check if we have to lookup a path to prefix */
490     if (RelativeTo != RTL_REGISTRY_ABSOLUTE)
491     {
492         /* Check if we need the current user key */
493         if (RelativeTo == RTL_REGISTRY_USER)
494         {
495             /* Get the user key path */
496             Status = RtlFormatCurrentUserKeyPath(&KeyPath);
497 
498             /* Check if it worked */
499             if (NT_SUCCESS(Status))
500             {
501                 /* Append the user key path */
502                 Status = RtlAppendUnicodeStringToString(&KeyName, &KeyPath);
503 
504                 /* Free the user key path */
505                 RtlFreeUnicodeString (&KeyPath);
506             }
507             else
508             {
509                 /* It didn't work so fall back to the default user key */
510                 Status = RtlAppendUnicodeToString(&KeyName, RtlpRegPaths[RTL_REGISTRY_USER]);
511             }
512         }
513         else
514         {
515             /* Get one of the prefixes */
516             Status = RtlAppendUnicodeToString(&KeyName,
517                                               RtlpRegPaths[RelativeTo]);
518         }
519 
520         /* Check for failure, otherwise, append the path separator */
521         if (!NT_SUCCESS(Status)) return Status;
522         Status = RtlAppendUnicodeToString(&KeyName, L"\\");
523         if (!NT_SUCCESS(Status)) return Status;
524     }
525 
526     /* And now append the path */
527     if (Path[0] == L'\\' && RelativeTo != RTL_REGISTRY_ABSOLUTE) Path++; // HACK!
528     Status = RtlAppendUnicodeToString(&KeyName, Path);
529     if (!NT_SUCCESS(Status)) return Status;
530 
531     /* Initialize the object attributes */
532     InitializeObjectAttributes(&ObjectAttributes,
533                                &KeyName,
534                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
535                                NULL,
536                                NULL);
537 
538     /* Check if we want to create it */
539     if (Create)
540     {
541         /* Create the key with write privileges */
542         Status = ZwCreateKey(KeyHandle,
543                              GENERIC_WRITE,
544                              &ObjectAttributes,
545                              0,
546                              NULL,
547                              0,
548                              NULL);
549     }
550     else
551     {
552         /* Otherwise, just open it with read access */
553         Status = ZwOpenKey(KeyHandle,
554                            MAXIMUM_ALLOWED | GENERIC_READ,
555                            &ObjectAttributes);
556     }
557 
558     /* Return status */
559     return Status;
560 }
561 
562 FORCEINLINE
563 VOID
RtlpCloseRegistryHandle(_In_ ULONG RelativeTo,_In_ HANDLE KeyHandle)564 RtlpCloseRegistryHandle(
565     _In_ ULONG RelativeTo,
566     _In_ HANDLE KeyHandle)
567 {
568     /* Did the caller pass a key handle? */
569     if (!(RelativeTo & RTL_REGISTRY_HANDLE))
570     {
571         /* We opened the key in RtlpGetRegistryHandle, so close it now */
572         ZwClose(KeyHandle);
573     }
574 }
575 
576 /* PUBLIC FUNCTIONS **********************************************************/
577 
578 /*
579  * @implemented
580  */
581 NTSTATUS
582 NTAPI
RtlCheckRegistryKey(IN ULONG RelativeTo,IN PWSTR Path)583 RtlCheckRegistryKey(IN ULONG RelativeTo,
584                     IN PWSTR Path)
585 {
586     HANDLE KeyHandle;
587     NTSTATUS Status;
588     PAGED_CODE_RTL();
589 
590     /* Call the helper */
591     Status = RtlpGetRegistryHandle(RelativeTo,
592                                    Path,
593                                    FALSE,
594                                    &KeyHandle);
595     if (!NT_SUCCESS(Status)) return Status;
596 
597     /* Close the handle even for RTL_REGISTRY_HANDLE */
598     ZwClose(KeyHandle);
599     return STATUS_SUCCESS;
600 }
601 
602 /*
603  * @implemented
604  */
605 NTSTATUS
606 NTAPI
RtlCreateRegistryKey(IN ULONG RelativeTo,IN PWSTR Path)607 RtlCreateRegistryKey(IN ULONG RelativeTo,
608                      IN PWSTR Path)
609 {
610     HANDLE KeyHandle;
611     NTSTATUS Status;
612     PAGED_CODE_RTL();
613 
614     /* Call the helper */
615     Status = RtlpGetRegistryHandle(RelativeTo,
616                                    Path,
617                                    TRUE,
618                                    &KeyHandle);
619     if (!NT_SUCCESS(Status)) return Status;
620 
621     /* All went well, close the handle and return status */
622     RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
623     return STATUS_SUCCESS;
624 }
625 
626 /*
627  * @implemented
628  */
629 NTSTATUS
630 NTAPI
RtlDeleteRegistryValue(IN ULONG RelativeTo,IN PCWSTR Path,IN PCWSTR ValueName)631 RtlDeleteRegistryValue(IN ULONG RelativeTo,
632                        IN PCWSTR Path,
633                        IN PCWSTR ValueName)
634 {
635     HANDLE KeyHandle;
636     NTSTATUS Status;
637     UNICODE_STRING Name;
638     PAGED_CODE_RTL();
639 
640     /* Call the helper */
641     Status = RtlpGetRegistryHandle(RelativeTo,
642                                    Path,
643                                    TRUE,
644                                    &KeyHandle);
645     if (!NT_SUCCESS(Status)) return Status;
646 
647     /* Initialize the key name and delete it */
648     RtlInitUnicodeString(&Name, ValueName);
649     Status = ZwDeleteValueKey(KeyHandle, &Name);
650 
651     /* Close the handle and return status */
652     RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
653     return Status;
654 }
655 
656 /*
657  * @implemented
658  */
659 NTSTATUS
660 NTAPI
RtlWriteRegistryValue(IN ULONG RelativeTo,IN PCWSTR Path,IN PCWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength)661 RtlWriteRegistryValue(IN ULONG RelativeTo,
662                       IN PCWSTR Path,
663                       IN PCWSTR ValueName,
664                       IN ULONG ValueType,
665                       IN PVOID ValueData,
666                       IN ULONG ValueLength)
667 {
668     HANDLE KeyHandle;
669     NTSTATUS Status;
670     UNICODE_STRING Name;
671     PAGED_CODE_RTL();
672 
673     /* Call the helper */
674     Status = RtlpGetRegistryHandle(RelativeTo,
675                                    Path,
676                                    TRUE,
677                                    &KeyHandle);
678     if (!NT_SUCCESS(Status)) return Status;
679 
680     /* Initialize the key name and set it */
681     RtlInitUnicodeString(&Name, ValueName);
682     Status = ZwSetValueKey(KeyHandle,
683                            &Name,
684                            0,
685                            ValueType,
686                            ValueData,
687                            ValueLength);
688 
689     /* Close the handle and return status */
690     RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
691     return Status;
692 }
693 
694 /*
695  * @implemented
696  */
697 NTSTATUS
698 NTAPI
RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,OUT PHANDLE KeyHandle)699 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,
700                    OUT PHANDLE KeyHandle)
701 {
702     OBJECT_ATTRIBUTES ObjectAttributes;
703     UNICODE_STRING KeyPath;
704     NTSTATUS Status;
705     PAGED_CODE_RTL();
706 
707     /* Get the user key */
708     Status = RtlFormatCurrentUserKeyPath(&KeyPath);
709     if (NT_SUCCESS(Status))
710     {
711         /* Initialize the attributes and open it */
712         InitializeObjectAttributes(&ObjectAttributes,
713                                    &KeyPath,
714                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
715                                    NULL,
716                                    NULL);
717         Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
718 
719         /* Free the path and return success if it worked */
720         RtlFreeUnicodeString(&KeyPath);
721         if (NT_SUCCESS(Status)) return STATUS_SUCCESS;
722     }
723 
724     /* It didn't work, so use the default key */
725     RtlInitUnicodeString(&KeyPath, RtlpRegPaths[RTL_REGISTRY_USER]);
726     InitializeObjectAttributes(&ObjectAttributes,
727                                &KeyPath,
728                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
729                                NULL,
730                                NULL);
731     Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
732 
733     /* Return status */
734     return Status;
735 }
736 
737 /*
738  * @implemented
739  */
740 NTSTATUS
741 NTAPI
RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath)742 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath)
743 {
744     HANDLE TokenHandle;
745     UCHAR Buffer[256];
746     PSID_AND_ATTRIBUTES SidBuffer;
747     ULONG Length;
748     UNICODE_STRING SidString;
749     NTSTATUS Status;
750     PAGED_CODE_RTL();
751 
752     /* Open the thread token */
753     Status = ZwOpenThreadTokenEx(NtCurrentThread(),
754                                  TOKEN_QUERY,
755                                  TRUE,
756                                  OBJ_KERNEL_HANDLE,
757                                  &TokenHandle);
758     if (!NT_SUCCESS(Status))
759     {
760         /* We failed, is it because we don't have a thread token? */
761         if (Status != STATUS_NO_TOKEN) return Status;
762 
763         /* It is, so use the process token */
764         Status = ZwOpenProcessTokenEx(NtCurrentProcess(),
765                                       TOKEN_QUERY,
766                                       OBJ_KERNEL_HANDLE,
767                                       &TokenHandle);
768         if (!NT_SUCCESS(Status)) return Status;
769     }
770 
771     /* Now query the token information */
772     SidBuffer = (PSID_AND_ATTRIBUTES)Buffer;
773     Status = ZwQueryInformationToken(TokenHandle,
774                                      TokenUser,
775                                      (PVOID)SidBuffer,
776                                      sizeof(Buffer),
777                                      &Length);
778 
779     /* Close the handle and handle failure */
780     ZwClose(TokenHandle);
781     if (!NT_SUCCESS(Status)) return Status;
782 
783     /* Convert the SID */
784     Status = RtlConvertSidToUnicodeString(&SidString, SidBuffer[0].Sid, TRUE);
785     if (!NT_SUCCESS(Status)) return Status;
786 
787     /* Add the length of the prefix */
788     Length = SidString.Length + sizeof(L"\\REGISTRY\\USER\\");
789 
790     /* Initialize a string */
791     RtlInitEmptyUnicodeString(KeyPath,
792                               RtlpAllocateStringMemory(Length, TAG_USTR),
793                               (USHORT)Length);
794     if (!KeyPath->Buffer)
795     {
796         /* Free the string and fail */
797         RtlFreeUnicodeString(&SidString);
798         return STATUS_NO_MEMORY;
799     }
800 
801     /* Append the prefix and SID */
802     RtlAppendUnicodeToString(KeyPath, L"\\REGISTRY\\USER\\");
803     RtlAppendUnicodeStringToString(KeyPath, &SidString);
804 
805     /* Free the temporary string and return success */
806     RtlFreeUnicodeString(&SidString);
807     return STATUS_SUCCESS;
808 }
809 
810 /*
811  * @implemented
812  */
813 NTSTATUS
814 NTAPI
RtlpNtCreateKey(OUT HANDLE KeyHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG TitleIndex,IN PUNICODE_STRING Class,OUT PULONG Disposition)815 RtlpNtCreateKey(OUT HANDLE KeyHandle,
816                 IN ACCESS_MASK DesiredAccess,
817                 IN POBJECT_ATTRIBUTES ObjectAttributes,
818                 IN ULONG TitleIndex,
819                 IN PUNICODE_STRING Class,
820                 OUT PULONG Disposition)
821 {
822     /* Check if we have object attributes */
823     if (ObjectAttributes)
824     {
825         /* Mask out the unsupported flags */
826         ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE);
827     }
828 
829     /* Create the key */
830     return ZwCreateKey(KeyHandle,
831                        DesiredAccess,
832                        ObjectAttributes,
833                        0,
834                        NULL,
835                        0,
836                        Disposition);
837 }
838 
839 /*
840  * @implemented
841  */
842 NTSTATUS
843 NTAPI
RtlpNtEnumerateSubKey(IN HANDLE KeyHandle,OUT PUNICODE_STRING SubKeyName,IN ULONG Index,IN ULONG Unused)844 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle,
845                       OUT PUNICODE_STRING SubKeyName,
846                       IN ULONG Index,
847                       IN ULONG Unused)
848 {
849     PKEY_BASIC_INFORMATION KeyInfo = NULL;
850     ULONG BufferLength = 0;
851     ULONG ReturnedLength;
852     NTSTATUS Status;
853 
854     /* Check if we have a name */
855     if (SubKeyName->MaximumLength)
856     {
857         /* Allocate a buffer for it */
858         BufferLength = SubKeyName->MaximumLength +
859                        sizeof(KEY_BASIC_INFORMATION);
860         KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
861         if (!KeyInfo) return STATUS_NO_MEMORY;
862     }
863 
864     /* Enumerate the key */
865     Status = ZwEnumerateKey(KeyHandle,
866                             Index,
867                             KeyBasicInformation,
868                             KeyInfo,
869                             BufferLength,
870                             &ReturnedLength);
871     if (NT_SUCCESS(Status) && (KeyInfo != NULL))
872     {
873         /* Check if the name fits */
874         if (KeyInfo->NameLength <= SubKeyName->MaximumLength)
875         {
876             /* Set the length */
877             SubKeyName->Length = (USHORT)KeyInfo->NameLength;
878 
879             /* Copy it */
880             RtlMoveMemory(SubKeyName->Buffer,
881                           KeyInfo->Name,
882                           SubKeyName->Length);
883         }
884         else
885         {
886             /* Otherwise, we ran out of buffer space */
887             Status = STATUS_BUFFER_OVERFLOW;
888         }
889     }
890 
891     /* Free the buffer and return status */
892     if (KeyInfo) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
893     return Status;
894 }
895 
896 /*
897  * @implemented
898  */
899 NTSTATUS
900 NTAPI
RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle)901 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle)
902 {
903     /* This just deletes the key */
904     return ZwDeleteKey(KeyHandle);
905 }
906 
907 /*
908  * @implemented
909  */
910 NTSTATUS
911 NTAPI
RtlpNtOpenKey(OUT HANDLE KeyHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG Unused)912 RtlpNtOpenKey(OUT HANDLE KeyHandle,
913               IN ACCESS_MASK DesiredAccess,
914               IN POBJECT_ATTRIBUTES ObjectAttributes,
915               IN ULONG Unused)
916 {
917     /* Check if we have object attributes */
918     if (ObjectAttributes)
919     {
920         /* Mask out the unsupported flags */
921         ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE);
922     }
923 
924     /* Open the key */
925     return ZwOpenKey(KeyHandle, DesiredAccess, ObjectAttributes);
926 }
927 
928 /*
929  * @implemented
930  */
931 NTSTATUS
932 NTAPI
RtlpNtQueryValueKey(IN HANDLE KeyHandle,OUT PULONG Type OPTIONAL,OUT PVOID Data OPTIONAL,IN OUT PULONG DataLength OPTIONAL,IN ULONG Unused)933 RtlpNtQueryValueKey(IN HANDLE KeyHandle,
934                     OUT PULONG Type OPTIONAL,
935                     OUT PVOID Data OPTIONAL,
936                     IN OUT PULONG DataLength OPTIONAL,
937                     IN ULONG Unused)
938 {
939     PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
940     UNICODE_STRING ValueName;
941     ULONG BufferLength = 0;
942     NTSTATUS Status;
943 
944     /* Clear the value name */
945     RtlInitEmptyUnicodeString(&ValueName, NULL, 0);
946 
947     /* Check if we were already given a length */
948     if (DataLength) BufferLength = *DataLength;
949 
950     /* Add the size of the structure */
951     BufferLength += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
952 
953     /* Allocate memory for the value */
954     ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
955     if (!ValueInfo) return STATUS_NO_MEMORY;
956 
957     /* Query the value */
958     Status = ZwQueryValueKey(KeyHandle,
959                              &ValueName,
960                              KeyValuePartialInformation,
961                              ValueInfo,
962                              BufferLength,
963                              &BufferLength);
964     if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW))
965     {
966         /* Return the length and type */
967         if (DataLength) *DataLength = ValueInfo->DataLength;
968         if (Type) *Type = ValueInfo->Type;
969     }
970 
971     /* Check if the caller wanted data back, and we got it */
972     if ((NT_SUCCESS(Status)) && (Data))
973     {
974         /* Copy it */
975         RtlMoveMemory(Data, ValueInfo->Data, ValueInfo->DataLength);
976     }
977 
978     /* Free the memory and return status */
979     RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo);
980     return Status;
981 }
982 
983 /*
984  * @implemented
985  */
986 NTSTATUS
987 NTAPI
RtlpNtSetValueKey(IN HANDLE KeyHandle,IN ULONG Type,IN PVOID Data,IN ULONG DataLength)988 RtlpNtSetValueKey(IN HANDLE KeyHandle,
989                   IN ULONG Type,
990                   IN PVOID Data,
991                   IN ULONG DataLength)
992 {
993     UNICODE_STRING ValueName;
994 
995     /* Set the value */
996     RtlInitEmptyUnicodeString(&ValueName, NULL, 0);
997     return ZwSetValueKey(KeyHandle,
998                          &ValueName,
999                          0,
1000                          Type,
1001                          Data,
1002                          DataLength);
1003 }
1004 
1005 /*
1006  * @implemented
1007  */
1008 NTSTATUS
1009 NTAPI
RtlQueryRegistryValues(IN ULONG RelativeTo,IN PCWSTR Path,IN PRTL_QUERY_REGISTRY_TABLE QueryTable,IN PVOID Context,IN PVOID Environment OPTIONAL)1010 RtlQueryRegistryValues(IN ULONG RelativeTo,
1011                        IN PCWSTR Path,
1012                        IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
1013                        IN PVOID Context,
1014                        IN PVOID Environment OPTIONAL)
1015 {
1016     NTSTATUS Status;
1017     PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL;
1018     HANDLE KeyHandle, CurrentKey;
1019     SIZE_T BufferSize, InfoSize;
1020     UNICODE_STRING KeyPath, KeyValueName;
1021     OBJECT_ATTRIBUTES ObjectAttributes;
1022     ULONG i, Value;
1023     ULONG ResultLength;
1024 
1025     /* Get the registry handle */
1026     Status = RtlpGetRegistryHandle(RelativeTo, Path, FALSE, &KeyHandle);
1027     if (!NT_SUCCESS(Status)) return Status;
1028 
1029     /* Initialize the path */
1030     RtlInitUnicodeString(&KeyPath,
1031                          (RelativeTo & RTL_REGISTRY_HANDLE) ? NULL : Path);
1032 
1033     /* Allocate a query buffer */
1034     BufferSize = RtlpAllocDeallocQueryBufferSize;
1035     KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, NULL, 0, &Status);
1036     if (!KeyValueInfo)
1037     {
1038         /* Close the handle if we have one and fail */
1039         RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
1040         return Status;
1041     }
1042 
1043     /* Set defaults */
1044     KeyValueInfo->DataOffset = 0;
1045     InfoSize = BufferSize - sizeof(UNICODE_NULL);
1046     CurrentKey = KeyHandle;
1047 
1048     /* Loop the query table */
1049     while ((QueryTable->QueryRoutine) ||
1050            (QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY |
1051                                  RTL_QUERY_REGISTRY_DIRECT)))
1052     {
1053         /* Check if the request is invalid */
1054         if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
1055             (!(QueryTable->Name) ||
1056              (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
1057              (QueryTable->QueryRoutine)))
1058         {
1059             /* Fail */
1060             Status = STATUS_INVALID_PARAMETER;
1061             break;
1062         }
1063 
1064         /* Check if we want a specific key */
1065         if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY |
1066                                  RTL_QUERY_REGISTRY_SUBKEY))
1067         {
1068             /* Check if we're working with another handle */
1069             if (CurrentKey != KeyHandle)
1070             {
1071                 /* Close our current key and use the top */
1072                 NtClose(CurrentKey);
1073                 CurrentKey = KeyHandle;
1074             }
1075         }
1076 
1077         /* Check if we're querying the subkey */
1078         if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY)
1079         {
1080             /* Make sure we have a name */
1081             if (!QueryTable->Name)
1082             {
1083                 /* Fail */
1084                 Status = STATUS_INVALID_PARAMETER;
1085             }
1086             else
1087             {
1088                 /* Initialize the name */
1089                 RtlInitUnicodeString(&KeyPath, QueryTable->Name);
1090 
1091                 /* Get the key handle */
1092                 InitializeObjectAttributes(&ObjectAttributes,
1093                                            &KeyPath,
1094                                            OBJ_CASE_INSENSITIVE |
1095                                            OBJ_KERNEL_HANDLE,
1096                                            KeyHandle,
1097                                            NULL);
1098                 Status = ZwOpenKey(&CurrentKey,
1099                                    MAXIMUM_ALLOWED,
1100                                    &ObjectAttributes);
1101                 if (NT_SUCCESS(Status))
1102                 {
1103                     /* If we have a query routine, go enumerate values */
1104                     if (QueryTable->QueryRoutine) goto ProcessValues;
1105                 }
1106             }
1107         }
1108         else if (QueryTable->Name)
1109         {
1110             /* Initialize the path */
1111             RtlInitUnicodeString(&KeyValueName, QueryTable->Name);
1112 
1113             /* Start query loop */
1114             i = 0;
1115             while (TRUE)
1116             {
1117                 /* Make sure we didn't retry too many times */
1118                 if (i++ > 4)
1119                 {
1120                     /* Fail */
1121                     DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1122                             "at line %d\n", __LINE__);
1123                     break;
1124                 }
1125 
1126                 /* Query key information */
1127                 Status = ZwQueryValueKey(CurrentKey,
1128                                          &KeyValueName,
1129                                          KeyValueFullInformation,
1130                                          KeyValueInfo,
1131                                          (ULONG)InfoSize,
1132                                          &ResultLength);
1133                 if (Status == STATUS_BUFFER_OVERFLOW)
1134                 {
1135                     /* Normalize status code */
1136                     Status = STATUS_BUFFER_TOO_SMALL;
1137                 }
1138 
1139                 /* Check for failure */
1140                 if (!NT_SUCCESS(Status))
1141                 {
1142                     /* Check if we didn't find it */
1143                     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1144                     {
1145                         /* Setup a default */
1146                         KeyValueInfo->Type = REG_NONE;
1147                         KeyValueInfo->DataLength = 0;
1148                         ResultLength = (ULONG)InfoSize;
1149 
1150                         /* Call the query routine */
1151                         Status = RtlpCallQueryRegistryRoutine(QueryTable,
1152                                                               KeyValueInfo,
1153                                                               &ResultLength,
1154                                                               Context,
1155                                                               Environment);
1156                     }
1157 
1158                     /* Check for buffer being too small */
1159                     if (Status == STATUS_BUFFER_TOO_SMALL)
1160                     {
1161                         /* Increase allocation size */
1162                         BufferSize = ResultLength +
1163                                      sizeof(ULONG_PTR) +
1164                                      sizeof(UNICODE_NULL);
1165                         KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
1166                                                                    KeyValueInfo,
1167                                                                    BufferSize,
1168                                                                    &Status);
1169                         if (!KeyValueInfo) break;
1170 
1171                         /* Update the data */
1172                         KeyValueInfo->DataOffset = 0;
1173                         InfoSize = BufferSize - sizeof(UNICODE_NULL);
1174                         continue;
1175                     }
1176                 }
1177                 else
1178                 {
1179                     /* Check if this is a multi-string */
1180                     if (KeyValueInfo->Type == REG_MULTI_SZ)
1181                     {
1182                         /* Add a null-char */
1183                         ((PWCHAR)KeyValueInfo)[ResultLength / sizeof(WCHAR)] = UNICODE_NULL;
1184                         KeyValueInfo->DataLength += sizeof(UNICODE_NULL);
1185                     }
1186 
1187                     /* Call the query routine */
1188                     ResultLength = (ULONG)InfoSize;
1189                     Status = RtlpCallQueryRegistryRoutine(QueryTable,
1190                                                           KeyValueInfo,
1191                                                           &ResultLength,
1192                                                           Context,
1193                                                           Environment);
1194 
1195                     /* Check for buffer being too small */
1196                     if (Status == STATUS_BUFFER_TOO_SMALL)
1197                     {
1198                         /* Increase allocation size */
1199                         BufferSize = ResultLength +
1200                                      sizeof(ULONG_PTR) +
1201                                      sizeof(UNICODE_NULL);
1202                         KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
1203                                                                    KeyValueInfo,
1204                                                                    BufferSize,
1205                                                                    &Status);
1206                         if (!KeyValueInfo) break;
1207 
1208                         /* Update the data */
1209                         KeyValueInfo->DataOffset = 0;
1210                         InfoSize = BufferSize - sizeof(UNICODE_NULL);
1211                         continue;
1212                     }
1213 
1214                     /* Check if we need to delete the key */
1215                     if ((NT_SUCCESS(Status)) &&
1216                         (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE))
1217                     {
1218                         /* Delete it */
1219                         ZwDeleteValueKey(CurrentKey, &KeyValueName);
1220                     }
1221                 }
1222 
1223                 /* We're done, break out */
1224                 break;
1225             }
1226         }
1227         else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE)
1228         {
1229             /* Just call the query routine */
1230             Status = QueryTable->QueryRoutine(NULL,
1231                                               REG_NONE,
1232                                               NULL,
1233                                               0,
1234                                               Context,
1235                                               QueryTable->EntryContext);
1236         }
1237         else
1238         {
1239 ProcessValues:
1240             /* Loop every value */
1241             i = Value = 0;
1242             while (TRUE)
1243             {
1244                 /* Enumerate the keys */
1245                 Status = ZwEnumerateValueKey(CurrentKey,
1246                                              Value,
1247                                              KeyValueFullInformation,
1248                                              KeyValueInfo,
1249                                              (ULONG)InfoSize,
1250                                              &ResultLength);
1251                 if (Status == STATUS_BUFFER_OVERFLOW)
1252                 {
1253                     /* Normalize the status */
1254                     Status = STATUS_BUFFER_TOO_SMALL;
1255                 }
1256 
1257                 /* Check if we found all the entries */
1258                 if (Status == STATUS_NO_MORE_ENTRIES)
1259                 {
1260                     /* Check if this was the first entry and caller needs it */
1261                     if (!(Value) &&
1262                          (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED))
1263                     {
1264                         /* Fail */
1265                         Status = STATUS_OBJECT_NAME_NOT_FOUND;
1266                     }
1267                     else
1268                     {
1269                         /* Otherwise, it's ok */
1270                         Status = STATUS_SUCCESS;
1271                     }
1272                     break;
1273                 }
1274 
1275                 /* Check if enumeration worked */
1276                 if (NT_SUCCESS(Status))
1277                 {
1278                     /* Call the query routine */
1279                     ResultLength = (ULONG)InfoSize;
1280                     Status = RtlpCallQueryRegistryRoutine(QueryTable,
1281                                                           KeyValueInfo,
1282                                                           &ResultLength,
1283                                                           Context,
1284                                                           Environment);
1285                 }
1286 
1287                 /* Check if the query failed */
1288                 if (Status == STATUS_BUFFER_TOO_SMALL)
1289                 {
1290                     /* Increase allocation size */
1291                     BufferSize = ResultLength +
1292                                  sizeof(ULONG_PTR) +
1293                                  sizeof(UNICODE_NULL);
1294                     KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
1295                                                                KeyValueInfo,
1296                                                                BufferSize,
1297                                                                &Status);
1298                     if (!KeyValueInfo) break;
1299 
1300                     /* Update the data */
1301                     KeyValueInfo->DataOffset = 0;
1302                     InfoSize = BufferSize - sizeof(UNICODE_NULL);
1303 
1304                     /* Try the value again unless it's been too many times */
1305                     if (i++ <= 4) continue;
1306                     break;
1307                 }
1308 
1309                 /* Break out if we failed */
1310                 if (!NT_SUCCESS(Status)) break;
1311 
1312                 /* Reset the number of retries and check if we need to delete */
1313                 i = 0;
1314                 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE)
1315                 {
1316                     /* Build the name */
1317                     RtlInitEmptyUnicodeString(&KeyValueName,
1318                                               KeyValueInfo->Name,
1319                                               (USHORT)KeyValueInfo->NameLength);
1320                     KeyValueName.Length = KeyValueName.MaximumLength;
1321 
1322                     /* Delete the key */
1323                     Status = ZwDeleteValueKey(CurrentKey, &KeyValueName);
1324                     if (NT_SUCCESS(Status)) Value--;
1325                 }
1326 
1327                 /* Go to the next value */
1328                 Value++;
1329             }
1330         }
1331 
1332         /* Check if we failed anywhere along the road */
1333         if (!NT_SUCCESS(Status)) break;
1334 
1335         /* Continue */
1336         QueryTable++;
1337     }
1338 
1339     /* Check if we need to close our handle */
1340     if (KeyHandle) RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
1341     if ((CurrentKey) && (CurrentKey != KeyHandle)) ZwClose(CurrentKey);
1342 
1343     /* Free our buffer and return status */
1344     RtlpAllocDeallocQueryBuffer(NULL, KeyValueInfo, BufferSize, NULL);
1345     return Status;
1346 }
1347 
1348 /* EOF */
1349