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
)
120 SIZE_T Length
, SpareLength
, c
;
121 ULONG RequiredLength
;
122 PCHAR SpareData
, DataEnd
;
124 PWCHAR Name
, p
, ValueEnd
;
127 BOOLEAN FoundExpander
= FALSE
;
128 UNICODE_STRING Source
, Destination
;
131 InfoLength
= *InfoSize
;
134 /* Check if there's no data */
135 if (KeyValueInfo
->DataOffset
== MAXULONG
)
137 /* Return proper status code */
138 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
139 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
142 /* Setup spare data pointers */
143 SpareData
= (PCHAR
)KeyValueInfo
;
144 SpareLength
= InfoLength
;
145 DataEnd
= SpareData
+ SpareLength
;
147 /* Check if there's no value or data */
148 if ((KeyValueInfo
->Type
== REG_NONE
) ||
149 (!(KeyValueInfo
->DataLength
) &&
150 (KeyValueInfo
->Type
== QueryTable
->DefaultType
)))
152 /* Check if there's no value */
153 if (QueryTable
->DefaultType
== REG_NONE
)
155 /* Return proper status code */
156 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
157 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
160 /* We can setup a default value... capture the defaults */
161 Name
= (PWCHAR
)QueryTable
->Name
;
162 Type
= QueryTable
->DefaultType
;
163 Data
= QueryTable
->DefaultData
;
164 Length
= QueryTable
->DefaultLength
;
167 /* No default length given, try to calculate it */
169 if ((Type
== REG_SZ
) || (Type
== REG_EXPAND_SZ
))
171 /* This is a string, count the characters */
173 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
175 else if (Type
== REG_MULTI_SZ
)
177 /* This is a multi-string, calculate all characters */
178 while (*p
) while (*p
++);
179 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
+ sizeof(UNICODE_NULL
);
185 /* Check if this isn't a direct return */
186 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
))
188 /* Check if we have length */
189 if (KeyValueInfo
->DataLength
)
191 /* Increase the spare data */
192 SpareData
+= KeyValueInfo
->DataOffset
+
193 KeyValueInfo
->DataLength
;
197 /* Otherwise, the spare data only has the name data */
198 SpareData
+= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
199 KeyValueInfo
->NameLength
;
202 /* Align the pointer and get new size of spare data */
203 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
204 SpareLength
= DataEnd
- SpareData
;
206 /* Check if we have space to copy the data */
207 RequiredLength
= KeyValueInfo
->NameLength
+ sizeof(UNICODE_NULL
);
208 if (SpareLength
< RequiredLength
)
210 /* Fail and return the missing length */
211 *InfoSize
= (ULONG
)(SpareData
- (PCHAR
)KeyValueInfo
) + RequiredLength
;
212 return STATUS_BUFFER_TOO_SMALL
;
215 /* Copy the data and null-terminate it */
216 Name
= (PWCHAR
)SpareData
;
217 RtlCopyMemory(Name
, KeyValueInfo
->Name
, KeyValueInfo
->NameLength
);
218 Name
[KeyValueInfo
->NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
220 /* Update the spare data information */
221 SpareData
+= RequiredLength
;
222 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
223 SpareLength
= DataEnd
- SpareData
;
227 /* Just return the name */
228 Name
= (PWCHAR
)QueryTable
->Name
;
231 /* Capture key data */
232 Type
= KeyValueInfo
->Type
;
233 Data
= (PVOID
)((ULONG_PTR
)KeyValueInfo
+ KeyValueInfo
->DataOffset
);
234 Length
= KeyValueInfo
->DataLength
;
237 /* Check if we're expanding */
238 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOEXPAND
))
240 /* Check if it's a multi-string */
241 if (Type
== REG_MULTI_SZ
)
243 /* Prepare defaults */
244 Status
= STATUS_SUCCESS
;
245 ValueEnd
= (PWSTR
)((ULONG_PTR
)Data
+ Length
- sizeof(UNICODE_NULL
));
248 /* Loop all strings */
251 /* Go to the next string */
254 /* Get the length and check if this is direct */
255 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
256 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
259 Status
= RtlpQueryRegistryDirect(REG_SZ
,
262 QueryTable
->EntryContext
);
263 QueryTable
->EntryContext
= (PVOID
)((ULONG_PTR
)QueryTable
->
265 sizeof(UNICODE_STRING
));
269 /* Call the custom routine */
270 Status
= QueryTable
->QueryRoutine(Name
,
275 QueryTable
->EntryContext
);
278 /* Normalize status */
279 if (Status
== STATUS_BUFFER_TOO_SMALL
) Status
= STATUS_SUCCESS
;
280 if (!NT_SUCCESS(Status
)) break;
282 /* Update data pointer */
290 /* Check if this is an expand string */
291 if ((Type
== REG_EXPAND_SZ
) && (Length
>= sizeof(WCHAR
)))
293 /* Try to find the expander */
294 c
= Length
- sizeof(UNICODE_NULL
);
298 /* Check if this is one */
302 FoundExpander
= TRUE
;
306 /* Continue in the buffer */
311 /* So check if we have one */
314 /* Setup the source string */
315 RtlInitEmptyUnicodeString(&Source
, Data
, (USHORT
)Length
);
316 Source
.Length
= Source
.MaximumLength
- sizeof(UNICODE_NULL
);
318 /* Setup the desination string */
319 RtlInitEmptyUnicodeString(&Destination
, (PWCHAR
)SpareData
, 0);
321 /* Check if we're out of space */
322 if (SpareLength
<= 0)
324 /* Then we don't have any space in our string */
325 Destination
.MaximumLength
= 0;
327 else if (SpareLength
<= MAXUSHORT
)
329 /* This is the good case, where we fit into a string */
330 Destination
.MaximumLength
= (USHORT
)SpareLength
;
331 Destination
.Buffer
[SpareLength
/ 2 - 1] = UNICODE_NULL
;
335 /* We can't fit into a string, so truncate */
336 Destination
.MaximumLength
= MAXUSHORT
;
337 Destination
.Buffer
[MAXUSHORT
/ 2 - 1] = UNICODE_NULL
;
340 /* Expand the strings and set our type as one string */
341 Status
= RtlExpandEnvironmentStrings_U(Environment
,
347 /* Check for success */
348 if (NT_SUCCESS(Status
))
350 /* Set the value name and length to our string */
351 Data
= Destination
.Buffer
;
352 Length
= Destination
.Length
+ sizeof(UNICODE_NULL
);
356 /* Check if our buffer is too small */
357 if (Status
== STATUS_BUFFER_TOO_SMALL
)
359 /* Set the required missing length */
360 *InfoSize
= (ULONG
)(SpareData
- (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
;
412 _Success_(return!=NULL
|| BufferSize
==0)
413 _When_(BufferSize
!=NULL
,__drv_allocatesMem(Mem
))
416 RtlpAllocDeallocQueryBuffer(
417 _In_opt_ PSIZE_T BufferSize
,
418 _In_opt_
__drv_freesMem(Mem
) PVOID OldBuffer
,
419 _In_ SIZE_T OldBufferSize
,
420 _Out_opt_
_On_failure_(_Post_satisfies_(*Status
< 0)) PNTSTATUS Status
)
425 if (Status
) *Status
= STATUS_SUCCESS
;
427 /* Free the old buffer */
428 if (OldBuffer
) RtlpFreeMemory(OldBuffer
, TAG_RTLREGISTRY
);
430 /* Check if we need to allocate a new one */
434 Buffer
= RtlpAllocateMemory(*BufferSize
, TAG_RTLREGISTRY
);
435 if (!(Buffer
) && (Status
)) *Status
= STATUS_NO_MEMORY
;
438 /* Return the pointer */
444 RtlpGetRegistryHandle(IN ULONG RelativeTo
,
447 IN PHANDLE KeyHandle
)
449 UNICODE_STRING KeyPath
, KeyName
;
450 WCHAR KeyBuffer
[MAX_PATH
];
451 OBJECT_ATTRIBUTES ObjectAttributes
;
454 /* Check if we just want the handle */
455 if (RelativeTo
& RTL_REGISTRY_HANDLE
)
457 *KeyHandle
= (HANDLE
)Path
;
458 return STATUS_SUCCESS
;
461 /* Check for optional flag */
462 if (RelativeTo
& RTL_REGISTRY_OPTIONAL
)
465 RelativeTo
&= ~RTL_REGISTRY_OPTIONAL
;
468 /* Fail on invalid parameter */
469 if (RelativeTo
>= RTL_REGISTRY_MAXIMUM
) return STATUS_INVALID_PARAMETER
;
471 /* Initialize the key name */
472 RtlInitEmptyUnicodeString(&KeyName
, KeyBuffer
, sizeof(KeyBuffer
));
474 /* Check if we have to lookup a path to prefix */
475 if (RelativeTo
!= RTL_REGISTRY_ABSOLUTE
)
477 /* Check if we need the current user key */
478 if (RelativeTo
== RTL_REGISTRY_USER
)
480 /* Get the user key path */
481 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
483 /* Check if it worked */
484 if (NT_SUCCESS(Status
))
486 /* Append the user key path */
487 Status
= RtlAppendUnicodeStringToString(&KeyName
, &KeyPath
);
489 /* Free the user key path */
490 RtlFreeUnicodeString (&KeyPath
);
494 /* It didn't work so fall back to the default user key */
495 Status
= RtlAppendUnicodeToString(&KeyName
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
500 /* Get one of the prefixes */
501 Status
= RtlAppendUnicodeToString(&KeyName
,
502 RtlpRegPaths
[RelativeTo
]);
505 /* Check for failure, otherwise, append the path separator */
506 if (!NT_SUCCESS(Status
)) return Status
;
507 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
508 if (!NT_SUCCESS(Status
)) return Status
;
511 /* And now append the path */
512 if (Path
[0] == L
'\\' && RelativeTo
!= RTL_REGISTRY_ABSOLUTE
) Path
++; // HACK!
513 Status
= RtlAppendUnicodeToString(&KeyName
, Path
);
514 if (!NT_SUCCESS(Status
)) return Status
;
516 /* Initialize the object attributes */
517 InitializeObjectAttributes(&ObjectAttributes
,
519 OBJ_CASE_INSENSITIVE
,
523 /* Check if we want to create it */
526 /* Create the key with write privileges */
527 Status
= ZwCreateKey(KeyHandle
,
537 /* Otherwise, just open it with read access */
538 Status
= ZwOpenKey(KeyHandle
,
539 MAXIMUM_ALLOWED
| GENERIC_READ
,
547 /* PUBLIC FUNCTIONS **********************************************************/
554 RtlCheckRegistryKey(IN ULONG RelativeTo
,
561 /* Call the helper */
562 Status
= RtlpGetRegistryHandle(RelativeTo
,
566 if (!NT_SUCCESS(Status
)) return Status
;
568 /* All went well, close the handle and return success */
570 return STATUS_SUCCESS
;
578 RtlCreateRegistryKey(IN ULONG RelativeTo
,
585 /* Call the helper */
586 Status
= RtlpGetRegistryHandle(RelativeTo
,
590 if (!NT_SUCCESS(Status
)) return Status
;
592 /* All went well, close the handle and return success */
594 return STATUS_SUCCESS
;
602 RtlDeleteRegistryValue(IN ULONG RelativeTo
,
611 /* Call the helper */
612 Status
= RtlpGetRegistryHandle(RelativeTo
,
616 if (!NT_SUCCESS(Status
)) return Status
;
618 /* Initialize the key name and delete it */
619 RtlInitUnicodeString(&Name
, ValueName
);
620 Status
= ZwDeleteValueKey(KeyHandle
, &Name
);
622 /* All went well, close the handle and return status */
632 RtlWriteRegistryValue(IN ULONG RelativeTo
,
637 IN ULONG ValueLength
)
644 /* Call the helper */
645 Status
= RtlpGetRegistryHandle(RelativeTo
,
649 if (!NT_SUCCESS(Status
)) return Status
;
651 /* Initialize the key name and set it */
652 RtlInitUnicodeString(&Name
, ValueName
);
653 Status
= ZwSetValueKey(KeyHandle
,
660 /* All went well, close the handle and return status */
670 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess
,
671 OUT PHANDLE KeyHandle
)
673 OBJECT_ATTRIBUTES ObjectAttributes
;
674 UNICODE_STRING KeyPath
;
678 /* Get the user key */
679 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
680 if (NT_SUCCESS(Status
))
682 /* Initialize the attributes and open it */
683 InitializeObjectAttributes(&ObjectAttributes
,
685 OBJ_CASE_INSENSITIVE
,
688 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
690 /* Free the path and return success if it worked */
691 RtlFreeUnicodeString(&KeyPath
);
692 if (NT_SUCCESS(Status
)) return STATUS_SUCCESS
;
695 /* It didn't work, so use the default key */
696 RtlInitUnicodeString(&KeyPath
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
697 InitializeObjectAttributes(&ObjectAttributes
,
699 OBJ_CASE_INSENSITIVE
,
702 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
713 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath
)
717 PSID_AND_ATTRIBUTES SidBuffer
;
719 UNICODE_STRING SidString
;
723 /* Open the thread token */
724 Status
= ZwOpenThreadToken(NtCurrentThread(),
728 if (!NT_SUCCESS(Status
))
730 /* We failed, is it because we don't have a thread token? */
731 if (Status
!= STATUS_NO_TOKEN
) return Status
;
733 /* It is, so use the process token */
734 Status
= ZwOpenProcessToken(NtCurrentProcess(),
737 if (!NT_SUCCESS(Status
)) return Status
;
740 /* Now query the token information */
741 SidBuffer
= (PSID_AND_ATTRIBUTES
)Buffer
;
742 Status
= ZwQueryInformationToken(TokenHandle
,
748 /* Close the handle and handle failure */
749 ZwClose(TokenHandle
);
750 if (!NT_SUCCESS(Status
)) return Status
;
752 /* Convert the SID */
753 Status
= RtlConvertSidToUnicodeString(&SidString
, SidBuffer
[0].Sid
, TRUE
);
754 if (!NT_SUCCESS(Status
)) return Status
;
756 /* Add the length of the prefix */
757 Length
= SidString
.Length
+ sizeof(L
"\\REGISTRY\\USER\\");
759 /* Initialize a string */
760 RtlInitEmptyUnicodeString(KeyPath
,
761 RtlpAllocateStringMemory(Length
, TAG_USTR
),
763 if (!KeyPath
->Buffer
)
765 /* Free the string and fail */
766 RtlFreeUnicodeString(&SidString
);
767 return STATUS_NO_MEMORY
;
770 /* Append the prefix and SID */
771 RtlAppendUnicodeToString(KeyPath
, L
"\\REGISTRY\\USER\\");
772 RtlAppendUnicodeStringToString(KeyPath
, &SidString
);
774 /* Free the temporary string and return success */
775 RtlFreeUnicodeString(&SidString
);
776 return STATUS_SUCCESS
;
784 RtlpNtCreateKey(OUT HANDLE KeyHandle
,
785 IN ACCESS_MASK DesiredAccess
,
786 IN POBJECT_ATTRIBUTES ObjectAttributes
,
788 IN PUNICODE_STRING Class
,
789 OUT PULONG Disposition
)
791 /* Check if we have object attributes */
792 if (ObjectAttributes
)
794 /* Mask out the unsupported flags */
795 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
799 return ZwCreateKey(KeyHandle
,
813 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle
,
814 OUT PUNICODE_STRING SubKeyName
,
818 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
819 ULONG BufferLength
= 0;
820 ULONG ReturnedLength
;
823 /* Check if we have a name */
824 if (SubKeyName
->MaximumLength
)
826 /* Allocate a buffer for it */
827 BufferLength
= SubKeyName
->MaximumLength
+
828 sizeof(KEY_BASIC_INFORMATION
);
829 KeyInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
830 if (!KeyInfo
) return STATUS_NO_MEMORY
;
833 /* Enumerate the key */
834 Status
= ZwEnumerateKey(KeyHandle
,
840 if (NT_SUCCESS(Status
) && (KeyInfo
!= NULL
))
842 /* Check if the name fits */
843 if (KeyInfo
->NameLength
<= SubKeyName
->MaximumLength
)
846 SubKeyName
->Length
= (USHORT
)KeyInfo
->NameLength
;
849 RtlMoveMemory(SubKeyName
->Buffer
,
855 /* Otherwise, we ran out of buffer space */
856 Status
= STATUS_BUFFER_OVERFLOW
;
860 /* Free the buffer and return status */
861 if (KeyInfo
) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo
);
870 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle
)
872 /* This just deletes the key */
873 return ZwDeleteKey(KeyHandle
);
881 RtlpNtOpenKey(OUT HANDLE KeyHandle
,
882 IN ACCESS_MASK DesiredAccess
,
883 IN POBJECT_ATTRIBUTES ObjectAttributes
,
886 /* Check if we have object attributes */
887 if (ObjectAttributes
)
889 /* Mask out the unsupported flags */
890 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
894 return ZwOpenKey(KeyHandle
, DesiredAccess
, ObjectAttributes
);
902 RtlpNtQueryValueKey(IN HANDLE KeyHandle
,
903 OUT PULONG Type OPTIONAL
,
904 OUT PVOID Data OPTIONAL
,
905 IN OUT PULONG DataLength OPTIONAL
,
908 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
909 UNICODE_STRING ValueName
;
910 ULONG BufferLength
= 0;
913 /* Clear the value name */
914 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
916 /* Check if we were already given a length */
917 if (DataLength
) BufferLength
= *DataLength
;
919 /* Add the size of the structure */
920 BufferLength
+= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
922 /* Allocate memory for the value */
923 ValueInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
924 if (!ValueInfo
) return STATUS_NO_MEMORY
;
926 /* Query the value */
927 Status
= ZwQueryValueKey(KeyHandle
,
929 KeyValuePartialInformation
,
933 if ((NT_SUCCESS(Status
)) || (Status
== STATUS_BUFFER_OVERFLOW
))
935 /* Return the length and type */
936 if (DataLength
) *DataLength
= ValueInfo
->DataLength
;
937 if (Type
) *Type
= ValueInfo
->Type
;
940 /* Check if the caller wanted data back, and we got it */
941 if ((NT_SUCCESS(Status
)) && (Data
))
944 RtlMoveMemory(Data
, ValueInfo
->Data
, ValueInfo
->DataLength
);
947 /* Free the memory and return status */
948 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo
);
957 RtlpNtSetValueKey(IN HANDLE KeyHandle
,
962 UNICODE_STRING ValueName
;
965 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
966 return ZwSetValueKey(KeyHandle
,
979 RtlQueryRegistryValues(IN ULONG RelativeTo
,
981 IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
983 IN PVOID Environment OPTIONAL
)
986 PKEY_VALUE_FULL_INFORMATION KeyValueInfo
= NULL
;
987 HANDLE KeyHandle
, CurrentKey
;
988 SIZE_T BufferSize
, InfoSize
;
989 UNICODE_STRING KeyPath
, KeyValueName
;
990 OBJECT_ATTRIBUTES ObjectAttributes
;
994 /* Get the registry handle */
995 Status
= RtlpGetRegistryHandle(RelativeTo
, Path
, FALSE
, &KeyHandle
);
996 if (!NT_SUCCESS(Status
)) return Status
;
998 /* Initialize the path */
999 RtlInitUnicodeString(&KeyPath
,
1000 (RelativeTo
& RTL_REGISTRY_HANDLE
) ? NULL
: Path
);
1002 /* Allocate a query buffer */
1003 BufferSize
= RtlpAllocDeallocQueryBufferSize
;
1004 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
, NULL
, 0, &Status
);
1007 /* Close the handle if we have one and fail */
1008 if (!(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1013 KeyValueInfo
->DataOffset
= 0;
1014 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1015 CurrentKey
= KeyHandle
;
1017 /* Loop the query table */
1018 while ((QueryTable
->QueryRoutine
) ||
1019 (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_SUBKEY
|
1020 RTL_QUERY_REGISTRY_DIRECT
)))
1022 /* Check if the request is invalid */
1023 if ((QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
) &&
1024 (!(QueryTable
->Name
) ||
1025 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
) ||
1026 (QueryTable
->QueryRoutine
)))
1029 Status
= STATUS_INVALID_PARAMETER
;
1033 /* Check if we want a specific key */
1034 if (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_TOPKEY
|
1035 RTL_QUERY_REGISTRY_SUBKEY
))
1037 /* Check if we're working with another handle */
1038 if (CurrentKey
!= KeyHandle
)
1040 /* Close our current key and use the top */
1041 NtClose(CurrentKey
);
1042 CurrentKey
= KeyHandle
;
1046 /* Check if we're querying the subkey */
1047 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
)
1049 /* Make sure we have a name */
1050 if (!QueryTable
->Name
)
1053 Status
= STATUS_INVALID_PARAMETER
;
1057 /* Initialize the name */
1058 RtlInitUnicodeString(&KeyPath
, QueryTable
->Name
);
1060 /* Get the key handle */
1061 InitializeObjectAttributes(&ObjectAttributes
,
1063 OBJ_CASE_INSENSITIVE
|
1067 Status
= ZwOpenKey(&CurrentKey
,
1070 if (NT_SUCCESS(Status
))
1072 /* If we have a query routine, go enumerate values */
1073 if (QueryTable
->QueryRoutine
) goto ProcessValues
;
1077 else if (QueryTable
->Name
)
1079 /* Initialize the path */
1080 RtlInitUnicodeString(&KeyValueName
, QueryTable
->Name
);
1082 /* Start query loop */
1086 /* Make sure we didn't retry too many times */
1090 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1091 "at line %d\n", __LINE__
);
1095 /* Query key information */
1096 Status
= ZwQueryValueKey(CurrentKey
,
1098 KeyValueFullInformation
,
1102 if (Status
== STATUS_BUFFER_OVERFLOW
)
1104 /* Normalize status code */
1105 Status
= STATUS_BUFFER_TOO_SMALL
;
1108 /* Check for failure */
1109 if (!NT_SUCCESS(Status
))
1111 /* Check if we didn't find it */
1112 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1114 /* Setup a default */
1115 KeyValueInfo
->Type
= REG_NONE
;
1116 KeyValueInfo
->DataLength
= 0;
1117 ResultLength
= (ULONG
)InfoSize
;
1119 /* Call the query routine */
1120 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1127 /* Check for buffer being too small */
1128 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1130 /* Increase allocation size */
1131 BufferSize
= ResultLength
+
1133 sizeof(UNICODE_NULL
);
1134 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1138 if (!KeyValueInfo
) break;
1140 /* Update the data */
1141 KeyValueInfo
->DataOffset
= 0;
1142 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1148 /* Check if this is a multi-string */
1149 if (KeyValueInfo
->Type
== REG_MULTI_SZ
)
1151 /* Add a null-char */
1152 ((PWCHAR
)KeyValueInfo
)[ResultLength
/ 2] = UNICODE_NULL
;
1153 KeyValueInfo
->DataLength
+= sizeof(UNICODE_NULL
);
1156 /* Call the query routine */
1157 ResultLength
= (ULONG
)InfoSize
;
1158 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1164 /* Check for buffer being too small */
1165 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1167 /* Increase allocation size */
1168 BufferSize
= ResultLength
+
1170 sizeof(UNICODE_NULL
);
1171 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1175 if (!KeyValueInfo
) break;
1177 /* Update the data */
1178 KeyValueInfo
->DataOffset
= 0;
1179 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1183 /* Check if we need to delete the key */
1184 if ((NT_SUCCESS(Status
)) &&
1185 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
))
1188 ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1192 /* We're done, break out */
1196 else if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOVALUE
)
1198 /* Just call the query routine */
1199 Status
= QueryTable
->QueryRoutine(NULL
,
1204 QueryTable
->EntryContext
);
1209 /* Loop every value */
1213 /* Enumerate the keys */
1214 Status
= ZwEnumerateValueKey(CurrentKey
,
1216 KeyValueFullInformation
,
1220 if (Status
== STATUS_BUFFER_OVERFLOW
)
1222 /* Normalize the status */
1223 Status
= STATUS_BUFFER_TOO_SMALL
;
1226 /* Check if we found all the entries */
1227 if (Status
== STATUS_NO_MORE_ENTRIES
)
1229 /* Check if this was the first entry and caller needs it */
1231 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
))
1234 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1238 /* Otherwise, it's ok */
1239 Status
= STATUS_SUCCESS
;
1244 /* Check if enumeration worked */
1245 if (NT_SUCCESS(Status
))
1247 /* Call the query routine */
1248 ResultLength
= (ULONG
)InfoSize
;
1249 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1256 /* Check if the query failed */
1257 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1259 /* Increase allocation size */
1260 BufferSize
= ResultLength
+
1262 sizeof(UNICODE_NULL
);
1263 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1267 if (!KeyValueInfo
) break;
1269 /* Update the data */
1270 KeyValueInfo
->DataOffset
= 0;
1271 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1273 /* Try the value again unless it's been too many times */
1274 if (i
++ <= 4) continue;
1278 /* Break out if we failed */
1279 if (!NT_SUCCESS(Status
)) break;
1281 /* Reset the number of retries and check if we need to delete */
1283 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
)
1285 /* Build the name */
1286 RtlInitEmptyUnicodeString(&KeyValueName
,
1288 (USHORT
)KeyValueInfo
->NameLength
);
1289 KeyValueName
.Length
= KeyValueName
.MaximumLength
;
1291 /* Delete the key */
1292 Status
= ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1293 if (NT_SUCCESS(Status
)) Value
--;
1296 /* Go to the next value */
1301 /* Check if we failed anywhere along the road */
1302 if (!NT_SUCCESS(Status
)) break;
1308 /* Check if we need to close our handle */
1309 if ((KeyHandle
) && !(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1310 if ((CurrentKey
) && (CurrentKey
!= KeyHandle
)) ZwClose(CurrentKey
);
1312 /* Free our buffer and return status */
1313 RtlpAllocDeallocQueryBuffer(NULL
, KeyValueInfo
, BufferSize
, NULL
);