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)
10 /* INCLUDES *****************************************************************/
16 #define TAG_RTLREGISTRY 'vrqR'
18 extern SIZE_T RtlpAllocDeallocQueryBufferSize
;
20 /* DATA **********************************************************************/
22 PCWSTR RtlpRegPaths
[RTL_REGISTRY_MAXIMUM
] =
25 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services",
26 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
27 L
"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
28 L
"\\Registry\\Machine\\Hardware\\DeviceMap",
29 L
"\\Registry\\User\\.Default",
32 /* PRIVATE FUNCTIONS *********************************************************/
36 RtlpQueryRegistryDirect(IN ULONG ValueType
,
41 USHORT ActualLength
= (USHORT
)ValueLength
;
42 PUNICODE_STRING ReturnString
= Buffer
;
43 PULONG Length
= Buffer
;
46 /* Check if this is a string */
47 if ((ValueType
== REG_SZ
) ||
48 (ValueType
== REG_EXPAND_SZ
) ||
49 (ValueType
== REG_MULTI_SZ
))
51 /* Normalize the length */
52 if (ValueLength
> MAXUSHORT
) ValueLength
= MAXUSHORT
;
54 /* Check if the return string has been allocated */
55 if (!ReturnString
->Buffer
)
58 ReturnString
->Buffer
= RtlpAllocateMemory(ActualLength
, TAG_RTLREGISTRY
);
59 if (!ReturnString
->Buffer
) return STATUS_NO_MEMORY
;
60 ReturnString
->MaximumLength
= ActualLength
;
62 else if (ActualLength
> ReturnString
->MaximumLength
)
64 /* The string the caller allocated is too small */
65 return STATUS_BUFFER_TOO_SMALL
;
69 RtlCopyMemory(ReturnString
->Buffer
, ValueData
, ActualLength
);
70 ReturnString
->Length
= ActualLength
- sizeof(UNICODE_NULL
);
72 else if (ValueLength
<= sizeof(ULONG
))
74 /* Check if we can just copy the data */
75 if ((Buffer
!= ValueData
) && (ValueLength
))
78 RtlCopyMemory(Buffer
, ValueData
, ValueLength
);
83 /* Check if the length is negative */
84 if ((LONG
)*Length
< 0)
86 /* Get the real length and copy the buffer */
87 RealLength
= -(LONG
)*Length
;
88 if (RealLength
< ValueLength
) return STATUS_BUFFER_TOO_SMALL
;
89 RtlCopyMemory(Buffer
, ValueData
, ValueLength
);
93 /* Check if there's space for the length and type, plus data */
94 if (*Length
< (2 * sizeof(ULONG
) + ValueLength
))
97 return STATUS_BUFFER_TOO_SMALL
;
100 /* Return the data */
101 *Length
++ = ValueLength
;
102 *Length
++ = ValueType
;
103 RtlCopyMemory(Length
, ValueData
, ValueLength
);
108 return STATUS_SUCCESS
;
113 RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
114 IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo
,
115 IN OUT PULONG InfoSize
,
117 IN PVOID Environment
)
119 ULONG InfoLength
, Length
, c
;
120 LONG RequiredLength
, SpareLength
;
121 PCHAR SpareData
, DataEnd
;
123 PWCHAR Name
, p
, ValueEnd
;
126 BOOLEAN FoundExpander
= FALSE
;
127 UNICODE_STRING Source
, Destination
;
130 InfoLength
= *InfoSize
;
133 /* Check if there's no data */
134 if (KeyValueInfo
->DataOffset
== MAXULONG
)
136 /* Return proper status code */
137 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
138 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
141 /* Setup spare data pointers */
142 SpareData
= (PCHAR
)KeyValueInfo
;
143 SpareLength
= InfoLength
;
144 DataEnd
= SpareData
+ SpareLength
;
146 /* Check if there's no value or data */
147 if ((KeyValueInfo
->Type
== REG_NONE
) ||
148 (!(KeyValueInfo
->DataLength
) &&
149 (KeyValueInfo
->Type
== QueryTable
->DefaultType
)))
151 /* Check if there's no value */
152 if (QueryTable
->DefaultType
== REG_NONE
)
154 /* Return proper status code */
155 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
156 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
159 /* We can setup a default value... capture the defaults */
160 Name
= (PWCHAR
)QueryTable
->Name
;
161 Type
= QueryTable
->DefaultType
;
162 Data
= QueryTable
->DefaultData
;
163 Length
= QueryTable
->DefaultLength
;
166 /* No default length given, try to calculate it */
168 if ((Type
== REG_SZ
) || (Type
== REG_EXPAND_SZ
))
170 /* This is a string, count the characters */
172 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
174 else if (Type
== REG_MULTI_SZ
)
176 /* This is a multi-string, calculate all characters */
177 while (*p
) while (*p
++);
178 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
+ sizeof(UNICODE_NULL
);
184 /* Check if this isn't a direct return */
185 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
))
187 /* Check if we have length */
188 if (KeyValueInfo
->DataLength
)
190 /* Increase the spare data */
191 SpareData
+= KeyValueInfo
->DataOffset
+
192 KeyValueInfo
->DataLength
;
196 /* Otherwise, the spare data only has the name data */
197 SpareData
+= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
198 KeyValueInfo
->NameLength
;
201 /* Align the pointer and get new size of spare data */
202 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
203 SpareLength
= DataEnd
- SpareData
;
205 /* Check if we have space to copy the data */
206 RequiredLength
= KeyValueInfo
->NameLength
+ sizeof(UNICODE_NULL
);
207 if (SpareLength
< RequiredLength
)
209 /* Fail and return the missing length */
210 *InfoSize
= SpareData
- (PCHAR
)KeyValueInfo
+ RequiredLength
;
211 return STATUS_BUFFER_TOO_SMALL
;
214 /* Copy the data and null-terminate it */
215 Name
= (PWCHAR
)SpareData
;
216 RtlCopyMemory(Name
, KeyValueInfo
->Name
, KeyValueInfo
->NameLength
);
217 Name
[KeyValueInfo
->NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
219 /* Update the spare data information */
220 SpareData
+= RequiredLength
;
221 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
222 SpareLength
= DataEnd
- SpareData
;
226 /* Just return the name */
227 Name
= (PWCHAR
)QueryTable
->Name
;
230 /* Capture key data */
231 Type
= KeyValueInfo
->Type
;
232 Data
= (PVOID
)((ULONG_PTR
)KeyValueInfo
+ KeyValueInfo
->DataOffset
);
233 Length
= KeyValueInfo
->DataLength
;
236 /* Check if we're expanding */
237 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOEXPAND
))
239 /* Check if it's a multi-string */
240 if (Type
== REG_MULTI_SZ
)
242 /* Prepare defaults */
243 Status
= STATUS_SUCCESS
;
244 ValueEnd
= (PWSTR
)((ULONG_PTR
)Data
+ Length
) - sizeof(UNICODE_NULL
);
247 /* Loop all strings */
250 /* Go to the next string */
253 /* Get the length and check if this is direct */
254 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
255 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
258 Status
= RtlpQueryRegistryDirect(REG_SZ
,
261 QueryTable
->EntryContext
);
262 QueryTable
->EntryContext
= (PVOID
)((ULONG_PTR
)QueryTable
->
264 sizeof(UNICODE_STRING
));
268 /* Call the custom routine */
269 Status
= QueryTable
->QueryRoutine(Name
,
274 QueryTable
->EntryContext
);
277 /* Normalize status */
278 if (Status
== STATUS_BUFFER_TOO_SMALL
) Status
= STATUS_SUCCESS
;
279 if (!NT_SUCCESS(Status
)) break;
281 /* Update data pointer */
289 /* Check if this is an expand string */
290 if ((Type
== REG_EXPAND_SZ
) && (Length
>= sizeof(WCHAR
)))
292 /* Try to find the expander */
293 c
= Length
- sizeof(UNICODE_NULL
);
297 /* Check if this is one */
301 FoundExpander
= TRUE
;
305 /* Continue in the buffer */
310 /* So check if we have one */
313 /* Setup the source string */
314 RtlInitEmptyUnicodeString(&Source
, Data
, Length
);
315 Source
.Length
= Source
.MaximumLength
- sizeof(UNICODE_NULL
);
317 /* Setup the desination string */
318 RtlInitEmptyUnicodeString(&Destination
, (PWCHAR
)SpareData
, 0);
320 /* Check if we're out of space */
321 if (SpareLength
<= 0)
323 /* Then we don't have any space in our string */
324 Destination
.MaximumLength
= 0;
326 else if (SpareLength
<= MAXUSHORT
)
328 /* This is the good case, where we fit into a string */
329 Destination
.MaximumLength
= SpareLength
;
330 Destination
.Buffer
[SpareLength
/ 2 - 1] = UNICODE_NULL
;
334 /* We can't fit into a string, so truncate */
335 Destination
.MaximumLength
= MAXUSHORT
;
336 Destination
.Buffer
[MAXUSHORT
/ 2 - 1] = UNICODE_NULL
;
339 /* Expand the strings and set our type as one string */
340 Status
= RtlExpandEnvironmentStrings_U(Environment
,
343 (PULONG
)&RequiredLength
);
346 /* Check for success */
347 if (NT_SUCCESS(Status
))
349 /* Set the value name and length to our string */
350 Data
= Destination
.Buffer
;
351 Length
= Destination
.Length
+ sizeof(UNICODE_NULL
);
355 /* Check if our buffer is too small */
356 if (Status
== STATUS_BUFFER_TOO_SMALL
)
358 /* Set the required missing length */
359 *InfoSize
= SpareData
-
360 (PCHAR
)KeyValueInfo
+
363 /* Notify debugger */
364 DPRINT1("RTL: Expand variables for %wZ failed - "
365 "Status == %lx Size %x > %x <%x>\n",
370 Destination
.MaximumLength
);
374 /* Notify debugger */
375 DPRINT1("RTL: Expand variables for %wZ failed - "
381 /* Return the status */
388 /* Check if this is a direct query */
389 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
391 /* Return the data */
392 Status
= RtlpQueryRegistryDirect(Type
,
395 QueryTable
->EntryContext
);
399 /* Call the query routine */
400 Status
= QueryTable
->QueryRoutine(Name
,
405 QueryTable
->EntryContext
);
408 /* Normalize and return status */
409 return (Status
== STATUS_BUFFER_TOO_SMALL
) ? STATUS_SUCCESS
: Status
;
414 RtlpAllocDeallocQueryBuffer(IN OUT PSIZE_T BufferSize
,
416 IN SIZE_T OldBufferSize
,
417 OUT PNTSTATUS Status
)
422 if (Status
) *Status
= STATUS_SUCCESS
;
424 /* Free the old buffer */
425 if (OldBuffer
) RtlpFreeMemory(OldBuffer
, TAG_RTLREGISTRY
);
427 /* Check if we need to allocate a new one */
431 Buffer
= RtlpAllocateMemory(*BufferSize
, TAG_RTLREGISTRY
);
432 if (!(Buffer
) && (Status
)) *Status
= STATUS_NO_MEMORY
;
435 /* Return the pointer */
441 RtlpGetRegistryHandle(IN ULONG RelativeTo
,
444 IN PHANDLE KeyHandle
)
446 UNICODE_STRING KeyPath
, KeyName
;
447 WCHAR KeyBuffer
[MAX_PATH
];
448 OBJECT_ATTRIBUTES ObjectAttributes
;
451 /* Check if we just want the handle */
452 if (RelativeTo
& RTL_REGISTRY_HANDLE
)
454 *KeyHandle
= (HANDLE
)Path
;
455 return STATUS_SUCCESS
;
458 /* Check for optional flag */
459 if (RelativeTo
& RTL_REGISTRY_OPTIONAL
)
462 RelativeTo
&= ~RTL_REGISTRY_OPTIONAL
;
465 /* Fail on invalid parameter */
466 if (RelativeTo
>= RTL_REGISTRY_MAXIMUM
) return STATUS_INVALID_PARAMETER
;
468 /* Initialize the key name */
469 RtlInitEmptyUnicodeString(&KeyName
, KeyBuffer
, sizeof(KeyBuffer
));
471 /* Check if we have to lookup a path to prefix */
472 if (RelativeTo
!= RTL_REGISTRY_ABSOLUTE
)
474 /* Check if we need the current user key */
475 if (RelativeTo
== RTL_REGISTRY_USER
)
477 /* Get the user key path */
478 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
480 /* Check if it worked */
481 if (NT_SUCCESS(Status
))
483 /* Append the user key path */
484 Status
= RtlAppendUnicodeStringToString(&KeyName
, &KeyPath
);
486 /* Free the user key path */
487 RtlFreeUnicodeString (&KeyPath
);
491 /* It didn't work so fall back to the default user key */
492 Status
= RtlAppendUnicodeToString(&KeyName
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
497 /* Get one of the prefixes */
498 Status
= RtlAppendUnicodeToString(&KeyName
,
499 RtlpRegPaths
[RelativeTo
]);
502 /* Check for failure, otherwise, append the path separator */
503 if (!NT_SUCCESS(Status
)) return Status
;
504 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
505 if (!NT_SUCCESS(Status
)) return Status
;
508 /* And now append the path */
509 if (Path
[0] == L
'\\' && RelativeTo
!= RTL_REGISTRY_ABSOLUTE
) Path
++; // HACK!
510 Status
= RtlAppendUnicodeToString(&KeyName
, Path
);
511 if (!NT_SUCCESS(Status
)) return Status
;
513 /* Initialize the object attributes */
514 InitializeObjectAttributes(&ObjectAttributes
,
516 OBJ_CASE_INSENSITIVE
,
520 /* Check if we want to create it */
523 /* Create the key with write privileges */
524 Status
= ZwCreateKey(KeyHandle
,
534 /* Otherwise, just open it with read access */
535 Status
= ZwOpenKey(KeyHandle
,
536 MAXIMUM_ALLOWED
| GENERIC_READ
,
544 /* PUBLIC FUNCTIONS **********************************************************/
551 RtlCheckRegistryKey(IN ULONG RelativeTo
,
558 /* Call the helper */
559 Status
= RtlpGetRegistryHandle(RelativeTo
,
563 if (!NT_SUCCESS(Status
)) return Status
;
565 /* All went well, close the handle and return success */
567 return STATUS_SUCCESS
;
575 RtlCreateRegistryKey(IN ULONG RelativeTo
,
582 /* Call the helper */
583 Status
= RtlpGetRegistryHandle(RelativeTo
,
587 if (!NT_SUCCESS(Status
)) return Status
;
589 /* All went well, close the handle and return success */
591 return STATUS_SUCCESS
;
599 RtlDeleteRegistryValue(IN ULONG RelativeTo
,
608 /* Call the helper */
609 Status
= RtlpGetRegistryHandle(RelativeTo
,
613 if (!NT_SUCCESS(Status
)) return Status
;
615 /* Initialize the key name and delete it */
616 RtlInitUnicodeString(&Name
, ValueName
);
617 Status
= ZwDeleteValueKey(KeyHandle
, &Name
);
619 /* All went well, close the handle and return status */
629 RtlWriteRegistryValue(IN ULONG RelativeTo
,
634 IN ULONG ValueLength
)
641 /* Call the helper */
642 Status
= RtlpGetRegistryHandle(RelativeTo
,
646 if (!NT_SUCCESS(Status
)) return Status
;
648 /* Initialize the key name and set it */
649 RtlInitUnicodeString(&Name
, ValueName
);
650 Status
= ZwSetValueKey(KeyHandle
,
657 /* All went well, close the handle and return status */
667 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess
,
668 OUT PHANDLE KeyHandle
)
670 OBJECT_ATTRIBUTES ObjectAttributes
;
671 UNICODE_STRING KeyPath
;
675 /* Get the user key */
676 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
677 if (NT_SUCCESS(Status
))
679 /* Initialize the attributes and open it */
680 InitializeObjectAttributes(&ObjectAttributes
,
682 OBJ_CASE_INSENSITIVE
,
685 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
687 /* Free the path and return success if it worked */
688 RtlFreeUnicodeString(&KeyPath
);
689 if (NT_SUCCESS(Status
)) return STATUS_SUCCESS
;
692 /* It didn't work, so use the default key */
693 RtlInitUnicodeString(&KeyPath
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
694 InitializeObjectAttributes(&ObjectAttributes
,
696 OBJ_CASE_INSENSITIVE
,
699 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
710 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath
)
714 PSID_AND_ATTRIBUTES SidBuffer
;
716 UNICODE_STRING SidString
;
720 /* Open the thread token */
721 Status
= ZwOpenThreadToken(NtCurrentThread(),
725 if (!NT_SUCCESS(Status
))
727 /* We failed, is it because we don't have a thread token? */
728 if (Status
!= STATUS_NO_TOKEN
) return Status
;
730 /* It is, so use the process token */
731 Status
= ZwOpenProcessToken(NtCurrentProcess(),
734 if (!NT_SUCCESS(Status
)) return Status
;
737 /* Now query the token information */
738 SidBuffer
= (PSID_AND_ATTRIBUTES
)Buffer
;
739 Status
= ZwQueryInformationToken(TokenHandle
,
745 /* Close the handle and handle failure */
746 ZwClose(TokenHandle
);
747 if (!NT_SUCCESS(Status
)) return Status
;
749 /* Convert the SID */
750 Status
= RtlConvertSidToUnicodeString(&SidString
, SidBuffer
[0].Sid
, TRUE
);
751 if (!NT_SUCCESS(Status
)) return Status
;
753 /* Add the length of the prefix */
754 Length
= SidString
.Length
+ sizeof(L
"\\REGISTRY\\USER\\");
756 /* Initialize a string */
757 RtlInitEmptyUnicodeString(KeyPath
,
758 RtlpAllocateStringMemory(Length
, TAG_USTR
),
760 if (!KeyPath
->Buffer
)
762 /* Free the string and fail */
763 RtlFreeUnicodeString(&SidString
);
764 return STATUS_NO_MEMORY
;
767 /* Append the prefix and SID */
768 RtlAppendUnicodeToString(KeyPath
, L
"\\REGISTRY\\USER\\");
769 RtlAppendUnicodeStringToString(KeyPath
, &SidString
);
771 /* Free the temporary string and return success */
772 RtlFreeUnicodeString(&SidString
);
773 return STATUS_SUCCESS
;
781 RtlpNtCreateKey(OUT HANDLE KeyHandle
,
782 IN ACCESS_MASK DesiredAccess
,
783 IN POBJECT_ATTRIBUTES ObjectAttributes
,
785 IN PUNICODE_STRING Class
,
786 OUT PULONG Disposition
)
788 /* Check if we have object attributes */
789 if (ObjectAttributes
)
791 /* Mask out the unsupported flags */
792 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
796 return ZwCreateKey(KeyHandle
,
810 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle
,
811 OUT PUNICODE_STRING SubKeyName
,
815 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
816 ULONG BufferLength
= 0;
817 ULONG ReturnedLength
;
820 /* Check if we have a name */
821 if (SubKeyName
->MaximumLength
)
823 /* Allocate a buffer for it */
824 BufferLength
= SubKeyName
->MaximumLength
+
825 sizeof(KEY_BASIC_INFORMATION
);
826 KeyInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
827 if (!KeyInfo
) return STATUS_NO_MEMORY
;
830 /* Enumerate the key */
831 Status
= ZwEnumerateKey(KeyHandle
,
837 if (NT_SUCCESS(Status
))
839 /* Check if the name fits */
840 if (KeyInfo
->NameLength
<= SubKeyName
->MaximumLength
)
843 SubKeyName
->Length
= KeyInfo
->NameLength
;
846 RtlMoveMemory(SubKeyName
->Buffer
,
852 /* Otherwise, we ran out of buffer space */
853 Status
= STATUS_BUFFER_OVERFLOW
;
857 /* Free the buffer and return status */
858 if (KeyInfo
) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo
);
867 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle
)
869 /* This just deletes the key */
870 return ZwDeleteKey(KeyHandle
);
878 RtlpNtOpenKey(OUT HANDLE KeyHandle
,
879 IN ACCESS_MASK DesiredAccess
,
880 IN POBJECT_ATTRIBUTES ObjectAttributes
,
883 /* Check if we have object attributes */
884 if (ObjectAttributes
)
886 /* Mask out the unsupported flags */
887 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
891 return ZwOpenKey(KeyHandle
, DesiredAccess
, ObjectAttributes
);
899 RtlpNtQueryValueKey(IN HANDLE KeyHandle
,
900 OUT PULONG Type OPTIONAL
,
901 OUT PVOID Data OPTIONAL
,
902 IN OUT PULONG DataLength OPTIONAL
,
905 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
906 UNICODE_STRING ValueName
;
907 ULONG BufferLength
= 0;
910 /* Clear the value name */
911 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
913 /* Check if we were already given a length */
914 if (DataLength
) BufferLength
= *DataLength
;
916 /* Add the size of the structure */
917 BufferLength
+= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
919 /* Allocate memory for the value */
920 ValueInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
921 if (!ValueInfo
) return STATUS_NO_MEMORY
;
923 /* Query the value */
924 Status
= ZwQueryValueKey(KeyHandle
,
926 KeyValuePartialInformation
,
930 if ((NT_SUCCESS(Status
)) || (Status
== STATUS_BUFFER_OVERFLOW
))
932 /* Return the length and type */
933 if (DataLength
) *DataLength
= ValueInfo
->DataLength
;
934 if (Type
) *Type
= ValueInfo
->Type
;
937 /* Check if the caller wanted data back, and we got it */
938 if ((NT_SUCCESS(Status
)) && (Data
))
941 RtlMoveMemory(Data
, ValueInfo
->Data
, ValueInfo
->DataLength
);
944 /* Free the memory and return status */
945 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo
);
954 RtlpNtSetValueKey(IN HANDLE KeyHandle
,
959 UNICODE_STRING ValueName
;
962 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
963 return ZwSetValueKey(KeyHandle
,
976 RtlQueryRegistryValues(IN ULONG RelativeTo
,
978 IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
980 IN PVOID Environment OPTIONAL
)
983 PKEY_VALUE_FULL_INFORMATION KeyValueInfo
= NULL
;
984 HANDLE KeyHandle
, CurrentKey
;
985 SIZE_T BufferSize
, InfoSize
;
986 UNICODE_STRING KeyPath
, KeyValueName
;
987 OBJECT_ATTRIBUTES ObjectAttributes
;
991 /* Get the registry handle */
992 Status
= RtlpGetRegistryHandle(RelativeTo
, Path
, FALSE
, &KeyHandle
);
993 if (!NT_SUCCESS(Status
)) return Status
;
995 /* Initialize the path */
996 RtlInitUnicodeString(&KeyPath
,
997 (RelativeTo
& RTL_REGISTRY_HANDLE
) ? NULL
: Path
);
999 /* Allocate a query buffer */
1000 BufferSize
= RtlpAllocDeallocQueryBufferSize
;
1001 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
, NULL
, 0, &Status
);
1004 /* Close the handle if we have one and fail */
1005 if (!(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1010 KeyValueInfo
->DataOffset
= 0;
1011 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1012 CurrentKey
= KeyHandle
;
1014 /* Loop the query table */
1015 while ((QueryTable
->QueryRoutine
) ||
1016 (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_SUBKEY
|
1017 RTL_QUERY_REGISTRY_DIRECT
)))
1019 /* Check if the request is invalid */
1020 if ((QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
) &&
1021 (!(QueryTable
->Name
) ||
1022 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
) ||
1023 (QueryTable
->QueryRoutine
)))
1026 Status
= STATUS_INVALID_PARAMETER
;
1030 /* Check if we want a specific key */
1031 if (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_TOPKEY
|
1032 RTL_QUERY_REGISTRY_SUBKEY
))
1034 /* Check if we're working with another handle */
1035 if (CurrentKey
!= KeyHandle
)
1037 /* Close our current key and use the top */
1038 NtClose(CurrentKey
);
1039 CurrentKey
= KeyHandle
;
1043 /* Check if we're querying the subkey */
1044 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
)
1046 /* Make sure we have a name */
1047 if (!QueryTable
->Name
)
1050 Status
= STATUS_INVALID_PARAMETER
;
1054 /* Initialize the name */
1055 RtlInitUnicodeString(&KeyPath
, QueryTable
->Name
);
1057 /* Get the key handle */
1058 InitializeObjectAttributes(&ObjectAttributes
,
1060 OBJ_CASE_INSENSITIVE
|
1064 Status
= ZwOpenKey(&CurrentKey
,
1067 if (NT_SUCCESS(Status
))
1069 /* If we have a query routine, go enumerate values */
1070 if (QueryTable
->QueryRoutine
) goto ProcessValues
;
1074 else if (QueryTable
->Name
)
1076 /* Initialize the path */
1077 RtlInitUnicodeString(&KeyValueName
, QueryTable
->Name
);
1079 /* Start query loop */
1083 /* Make sure we didn't retry too many times */
1087 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1088 "at line %d\n", __LINE__
);
1092 /* Query key information */
1093 Status
= ZwQueryValueKey(CurrentKey
,
1095 KeyValueFullInformation
,
1099 if (Status
== STATUS_BUFFER_OVERFLOW
)
1101 /* Normalize status code */
1102 Status
= STATUS_BUFFER_TOO_SMALL
;
1105 /* Check for failure */
1106 if (!NT_SUCCESS(Status
))
1108 /* Check if we didn't find it */
1109 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1111 /* Setup a default */
1112 KeyValueInfo
->Type
= REG_NONE
;
1113 KeyValueInfo
->DataLength
= 0;
1114 ResultLength
= InfoSize
;
1116 /* Call the query routine */
1117 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1124 /* Check for buffer being too small */
1125 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1127 /* Increase allocation size */
1128 BufferSize
= ResultLength
+
1130 sizeof(UNICODE_NULL
);
1131 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1135 if (!KeyValueInfo
) break;
1137 /* Update the data */
1138 KeyValueInfo
->DataOffset
= 0;
1139 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1145 /* Check if this is a multi-string */
1146 if (KeyValueInfo
->Type
== REG_MULTI_SZ
)
1148 /* Add a null-char */
1149 ((PWCHAR
)KeyValueInfo
)[ResultLength
/ 2] = UNICODE_NULL
;
1150 KeyValueInfo
->DataLength
+= sizeof(UNICODE_NULL
);
1153 /* Call the query routine */
1154 ResultLength
= InfoSize
;
1155 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1161 /* Check for buffer being too small */
1162 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1164 /* Increase allocation size */
1165 BufferSize
= ResultLength
+
1167 sizeof(UNICODE_NULL
);
1168 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1172 if (!KeyValueInfo
) break;
1174 /* Update the data */
1175 KeyValueInfo
->DataOffset
= 0;
1176 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1180 /* Check if we need to delete the key */
1181 if ((NT_SUCCESS(Status
)) &&
1182 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
))
1185 ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1189 /* We're done, break out */
1193 else if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOVALUE
)
1195 /* Just call the query routine */
1196 Status
= QueryTable
->QueryRoutine(NULL
,
1201 QueryTable
->EntryContext
);
1206 /* Loop every value */
1210 /* Enumerate the keys */
1211 Status
= ZwEnumerateValueKey(CurrentKey
,
1213 KeyValueFullInformation
,
1217 if (Status
== STATUS_BUFFER_OVERFLOW
)
1219 /* Normalize the status */
1220 Status
= STATUS_BUFFER_TOO_SMALL
;
1223 /* Check if we found all the entries */
1224 if (Status
== STATUS_NO_MORE_ENTRIES
)
1226 /* Check if this was the first entry and caller needs it */
1228 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
))
1231 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1235 /* Otherwise, it's ok */
1236 Status
= STATUS_SUCCESS
;
1241 /* Check if enumeration worked */
1242 if (NT_SUCCESS(Status
))
1244 /* Call the query routine */
1245 ResultLength
= InfoSize
;
1246 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1253 /* Check if the query failed */
1254 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1256 /* Increase allocation size */
1257 BufferSize
= ResultLength
+
1259 sizeof(UNICODE_NULL
);
1260 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1264 if (!KeyValueInfo
) break;
1266 /* Update the data */
1267 KeyValueInfo
->DataOffset
= 0;
1268 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1270 /* Try the value again unless it's been too many times */
1271 if (i
++ <= 4) continue;
1275 /* Break out if we failed */
1276 if (!NT_SUCCESS(Status
)) break;
1278 /* Reset the number of retries and check if we need to delete */
1280 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
)
1282 /* Build the name */
1283 RtlInitEmptyUnicodeString(&KeyValueName
,
1285 (USHORT
)KeyValueInfo
->NameLength
);
1286 KeyValueName
.Length
= KeyValueName
.MaximumLength
;
1288 /* Delete the key */
1289 Status
= ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1290 if (NT_SUCCESS(Status
)) Value
--;
1293 /* Go to the next value */
1298 /* Check if we failed anywhere along the road */
1299 if (!NT_SUCCESS(Status
)) break;
1305 /* Check if we need to close our handle */
1306 if ((KeyHandle
) && !(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1307 if ((CurrentKey
) && (CurrentKey
!= KeyHandle
)) ZwClose(CurrentKey
);
1309 /* Free our buffer and return status */
1310 RtlpAllocDeallocQueryBuffer(NULL
, KeyValueInfo
, BufferSize
, NULL
);