xref: /reactos/sdk/lib/rtl/registry.c (revision 40462c92)
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
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 = RtlpAllocateMemory(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
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 this isn't a direct return */
200         if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT))
201         {
202             /* Check if we have length */
203             if (KeyValueInfo->DataLength)
204             {
205                 /* Increase the spare data */
206                 SpareData += KeyValueInfo->DataOffset +
207                              KeyValueInfo->DataLength;
208             }
209             else
210             {
211                 /* Otherwise, the spare data only has the name data */
212                 SpareData += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
213                              KeyValueInfo->NameLength;
214             }
215 
216             /* Align the pointer and get new size of spare data */
217             SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
218             SpareLength = DataEnd - SpareData;
219 
220             /* Check if we have space to copy the data */
221             RequiredLength = KeyValueInfo->NameLength + sizeof(UNICODE_NULL);
222             if ((SpareData > DataEnd) || (SpareLength < RequiredLength))
223             {
224                 /* Fail and return the missing length */
225                 *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + RequiredLength;
226                 return STATUS_BUFFER_TOO_SMALL;
227             }
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 desination 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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