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