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 TAG('R', 'q', 'r', 'v')
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
== (ULONG
)-1)
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
)
478 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
479 if (!NT_SUCCESS(Status
)) return(Status
);
482 Status
= RtlAppendUnicodeStringToString(&KeyName
, &KeyPath
);
483 RtlFreeUnicodeString (&KeyPath
);
487 /* Get one of the prefixes */
488 Status
= RtlAppendUnicodeToString(&KeyName
,
489 RtlpRegPaths
[RelativeTo
]);
492 /* Check for failure, otherwise, append the path separator */
493 if (!NT_SUCCESS(Status
)) return Status
;
494 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
495 if (!NT_SUCCESS(Status
)) return Status
;
498 /* And now append the path */
499 if (Path
[0] == L
'\\' && RelativeTo
!= RTL_REGISTRY_ABSOLUTE
) Path
++; // HACK!
500 Status
= RtlAppendUnicodeToString(&KeyName
, Path
);
501 if (!NT_SUCCESS(Status
)) return Status
;
503 /* Initialize the object attributes */
504 InitializeObjectAttributes(&ObjectAttributes
,
506 OBJ_CASE_INSENSITIVE
,
510 /* Check if we want to create it */
513 /* Create the key with write privileges */
514 Status
= ZwCreateKey(KeyHandle
,
524 /* Otherwise, just open it with read access */
525 Status
= ZwOpenKey(KeyHandle
,
526 MAXIMUM_ALLOWED
| GENERIC_READ
,
534 /* PUBLIC FUNCTIONS **********************************************************/
541 RtlCheckRegistryKey(IN ULONG RelativeTo
,
548 /* Call the helper */
549 Status
= RtlpGetRegistryHandle(RelativeTo
,
553 if (!NT_SUCCESS(Status
)) return Status
;
555 /* All went well, close the handle and return success */
557 return STATUS_SUCCESS
;
565 RtlCreateRegistryKey(IN ULONG RelativeTo
,
572 /* Call the helper */
573 Status
= RtlpGetRegistryHandle(RelativeTo
,
577 if (!NT_SUCCESS(Status
)) return Status
;
579 /* All went well, close the handle and return success */
581 return STATUS_SUCCESS
;
589 RtlDeleteRegistryValue(IN ULONG RelativeTo
,
598 /* Call the helper */
599 Status
= RtlpGetRegistryHandle(RelativeTo
,
603 if (!NT_SUCCESS(Status
)) return Status
;
605 /* Initialize the key name and delete it */
606 RtlInitUnicodeString(&Name
, ValueName
);
607 Status
= ZwDeleteValueKey(KeyHandle
, &Name
);
609 /* All went well, close the handle and return status */
619 RtlWriteRegistryValue(IN ULONG RelativeTo
,
624 IN ULONG ValueLength
)
631 /* Call the helper */
632 Status
= RtlpGetRegistryHandle(RelativeTo
,
636 if (!NT_SUCCESS(Status
)) return Status
;
638 /* Initialize the key name and set it */
639 RtlInitUnicodeString(&Name
, ValueName
);
640 Status
= ZwSetValueKey(KeyHandle
,
647 /* All went well, close the handle and return status */
657 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess
,
658 OUT PHANDLE KeyHandle
)
660 OBJECT_ATTRIBUTES ObjectAttributes
;
661 UNICODE_STRING KeyPath
;
665 /* Get the user key */
666 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
667 if (NT_SUCCESS(Status
))
669 /* Initialize the attributes and open it */
670 InitializeObjectAttributes(&ObjectAttributes
,
672 OBJ_CASE_INSENSITIVE
,
675 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
677 /* Free the path and return success if it worked */
678 RtlFreeUnicodeString(&KeyPath
);
679 if (NT_SUCCESS(Status
)) return STATUS_SUCCESS
;
682 /* It didn't work, so use the default key */
683 RtlInitUnicodeString(&KeyPath
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
684 InitializeObjectAttributes(&ObjectAttributes
,
686 OBJ_CASE_INSENSITIVE
,
689 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
700 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath
)
704 PSID_AND_ATTRIBUTES SidBuffer
;
706 UNICODE_STRING SidString
;
710 /* Open the thread token */
711 Status
= ZwOpenThreadToken(NtCurrentThread(),
715 if (!NT_SUCCESS(Status
))
717 /* We failed, is it because we don't have a thread token? */
718 if (Status
!= STATUS_NO_TOKEN
) return Status
;
720 /* It is, so use the process token */
721 Status
= ZwOpenProcessToken(NtCurrentProcess(),
724 if (!NT_SUCCESS(Status
)) return Status
;
727 /* Now query the token information */
728 SidBuffer
= (PSID_AND_ATTRIBUTES
)Buffer
;
729 Status
= ZwQueryInformationToken(TokenHandle
,
735 /* Close the handle and handle failure */
736 ZwClose(TokenHandle
);
737 if (!NT_SUCCESS(Status
)) return Status
;
739 /* Convert the SID */
740 Status
= RtlConvertSidToUnicodeString(&SidString
, SidBuffer
[0].Sid
, TRUE
);
741 if (!NT_SUCCESS(Status
)) return Status
;
743 /* Add the length of the prefix */
744 Length
= SidString
.Length
+ sizeof(L
"\\REGISTRY\\USER\\");
746 /* Initialize a string */
747 RtlInitEmptyUnicodeString(KeyPath
,
748 RtlpAllocateStringMemory(Length
, TAG_USTR
),
750 if (!KeyPath
->Buffer
)
752 /* Free the string and fail */
753 RtlFreeUnicodeString(&SidString
);
754 return STATUS_NO_MEMORY
;
757 /* Append the prefix and SID */
758 RtlAppendUnicodeToString(KeyPath
, L
"\\REGISTRY\\USER\\");
759 RtlAppendUnicodeStringToString(KeyPath
, &SidString
);
761 /* Free the temporary string and return success */
762 RtlFreeUnicodeString(&SidString
);
763 return STATUS_SUCCESS
;
771 RtlpNtCreateKey(OUT HANDLE KeyHandle
,
772 IN ACCESS_MASK DesiredAccess
,
773 IN POBJECT_ATTRIBUTES ObjectAttributes
,
775 IN PUNICODE_STRING Class
,
776 OUT PULONG Disposition
)
778 /* Check if we have object attributes */
779 if (ObjectAttributes
)
781 /* Mask out the unsupported flags */
782 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
786 return ZwCreateKey(KeyHandle
,
800 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle
,
801 OUT PUNICODE_STRING SubKeyName
,
805 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
806 ULONG BufferLength
= 0;
807 ULONG ReturnedLength
;
810 /* Check if we have a name */
811 if (SubKeyName
->MaximumLength
)
813 /* Allocate a buffer for it */
814 BufferLength
= SubKeyName
->MaximumLength
+
815 sizeof(KEY_BASIC_INFORMATION
);
816 KeyInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
817 if (!KeyInfo
) return STATUS_NO_MEMORY
;
820 /* Enumerate the key */
821 Status
= ZwEnumerateKey(KeyHandle
,
827 if (NT_SUCCESS(Status
))
829 /* Check if the name fits */
830 if (KeyInfo
->NameLength
<= SubKeyName
->MaximumLength
)
833 SubKeyName
->Length
= KeyInfo
->NameLength
;
836 RtlMoveMemory(SubKeyName
->Buffer
,
842 /* Otherwise, we ran out of buffer space */
843 Status
= STATUS_BUFFER_OVERFLOW
;
847 /* Free the buffer and return status */
848 if (KeyInfo
) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo
);
857 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle
)
859 /* This just deletes the key */
860 return ZwDeleteKey(KeyHandle
);
868 RtlpNtOpenKey(OUT HANDLE KeyHandle
,
869 IN ACCESS_MASK DesiredAccess
,
870 IN POBJECT_ATTRIBUTES ObjectAttributes
,
873 /* Check if we have object attributes */
874 if (ObjectAttributes
)
876 /* Mask out the unsupported flags */
877 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
881 return ZwOpenKey(KeyHandle
, DesiredAccess
, ObjectAttributes
);
889 RtlpNtQueryValueKey(IN HANDLE KeyHandle
,
890 OUT PULONG Type OPTIONAL
,
891 OUT PVOID Data OPTIONAL
,
892 IN OUT PULONG DataLength OPTIONAL
,
895 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
896 UNICODE_STRING ValueName
;
897 ULONG BufferLength
= 0;
900 /* Clear the value name */
901 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
903 /* Check if we were already given a length */
904 if (DataLength
) BufferLength
= *DataLength
;
906 /* Add the size of the structure */
907 BufferLength
+= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
909 /* Allocate memory for the value */
910 ValueInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
911 if (!ValueInfo
) return STATUS_NO_MEMORY
;
913 /* Query the value */
914 Status
= ZwQueryValueKey(KeyHandle
,
916 KeyValuePartialInformation
,
920 if ((NT_SUCCESS(Status
)) || (Status
== STATUS_BUFFER_OVERFLOW
))
922 /* Return the length and type */
923 if (DataLength
) *DataLength
= ValueInfo
->DataLength
;
924 if (Type
) *Type
= ValueInfo
->Type
;
927 /* Check if the caller wanted data back, and we got it */
928 if ((NT_SUCCESS(Status
)) && (Data
))
931 RtlMoveMemory(Data
, ValueInfo
->Data
, ValueInfo
->DataLength
);
934 /* Free the memory and return status */
935 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo
);
944 RtlpNtSetValueKey(IN HANDLE KeyHandle
,
949 UNICODE_STRING ValueName
;
952 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
953 return ZwSetValueKey(KeyHandle
,
966 RtlQueryRegistryValues(IN ULONG RelativeTo
,
968 IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
970 IN PVOID Environment OPTIONAL
)
973 PKEY_VALUE_FULL_INFORMATION KeyValueInfo
= NULL
;
974 HANDLE KeyHandle
, CurrentKey
;
975 SIZE_T BufferSize
, InfoSize
;
976 UNICODE_STRING KeyPath
, KeyValueName
;
977 OBJECT_ATTRIBUTES ObjectAttributes
;
981 /* Get the registry handle */
982 Status
= RtlpGetRegistryHandle(RelativeTo
, Path
, FALSE
, &KeyHandle
);
983 if (!NT_SUCCESS(Status
)) return Status
;
985 /* Initialize the path */
986 RtlInitUnicodeString(&KeyPath
,
987 (RelativeTo
& RTL_REGISTRY_HANDLE
) ? NULL
: Path
);
989 /* Allocate a query buffer */
990 BufferSize
= RtlpAllocDeallocQueryBufferSize
;
991 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
, NULL
, 0, &Status
);
994 /* Close the handle if we have one and fail */
995 if (!(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1000 KeyValueInfo
->DataOffset
= 0;
1001 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1002 CurrentKey
= KeyHandle
;
1004 /* Loop the query table */
1005 while ((QueryTable
->QueryRoutine
) ||
1006 (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_SUBKEY
|
1007 RTL_QUERY_REGISTRY_DIRECT
)))
1009 /* Check if the request is invalid */
1010 if ((QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
) &&
1011 (!(QueryTable
->Name
) ||
1012 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
) ||
1013 (QueryTable
->QueryRoutine
)))
1016 Status
= STATUS_INVALID_PARAMETER
;
1020 /* Check if we want a specific key */
1021 if (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_TOPKEY
|
1022 RTL_QUERY_REGISTRY_SUBKEY
))
1024 /* Check if we're working with another handle */
1025 if (CurrentKey
!= KeyHandle
)
1027 /* Close our current key and use the top */
1028 NtClose(CurrentKey
);
1029 CurrentKey
= KeyHandle
;
1033 /* Check if we're querying the subkey */
1034 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
)
1036 /* Make sure we have a name */
1037 if (!QueryTable
->Name
)
1040 Status
= STATUS_INVALID_PARAMETER
;
1044 /* Initialize the name */
1045 RtlInitUnicodeString(&KeyPath
, QueryTable
->Name
);
1047 /* Get the key handle */
1048 InitializeObjectAttributes(&ObjectAttributes
,
1050 OBJ_CASE_INSENSITIVE
|
1054 Status
= ZwOpenKey(&CurrentKey
,
1057 if (NT_SUCCESS(Status
))
1059 /* If we have a query routine, go enumerate values */
1060 if (QueryTable
->QueryRoutine
) goto ProcessValues
;
1064 else if (QueryTable
->Name
)
1066 /* Initialize the path */
1067 RtlInitUnicodeString(&KeyValueName
, QueryTable
->Name
);
1069 /* Start query loop */
1073 /* Make sure we didn't retry too many times */
1077 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1078 "at line %d\n", __LINE__
);
1082 /* Query key information */
1083 Status
= ZwQueryValueKey(CurrentKey
,
1085 KeyValueFullInformation
,
1089 if (Status
== STATUS_BUFFER_OVERFLOW
)
1091 /* Normalize status code */
1092 Status
= STATUS_BUFFER_TOO_SMALL
;
1095 /* Check for failure */
1096 if (!NT_SUCCESS(Status
))
1098 /* Check if we didn't find it */
1099 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1101 /* Setup a default */
1102 KeyValueInfo
->Type
= REG_NONE
;
1103 KeyValueInfo
->DataLength
= 0;
1104 ResultLength
= InfoSize
;
1106 /* Call the query routine */
1107 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1114 /* Check for buffer being too small */
1115 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1117 /* Increase allocation size */
1118 BufferSize
= ResultLength
+
1120 sizeof(UNICODE_NULL
);
1121 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1125 if (!KeyValueInfo
) break;
1127 /* Update the data */
1128 KeyValueInfo
->DataOffset
= 0;
1129 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1135 /* Check if this is a multi-string */
1136 if (KeyValueInfo
->Type
== REG_MULTI_SZ
)
1138 /* Add a null-char */
1139 ((PWCHAR
)KeyValueInfo
)[ResultLength
/ 2] = UNICODE_NULL
;
1140 KeyValueInfo
->DataLength
+= sizeof(UNICODE_NULL
);
1143 /* Call the query routine */
1144 ResultLength
= InfoSize
;
1145 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1151 /* Check for buffer being too small */
1152 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1154 /* Increase allocation size */
1155 BufferSize
= ResultLength
+
1157 sizeof(UNICODE_NULL
);
1158 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1162 if (!KeyValueInfo
) break;
1164 /* Update the data */
1165 KeyValueInfo
->DataOffset
= 0;
1166 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1170 /* Check if we need to delete the key */
1171 if ((NT_SUCCESS(Status
)) &&
1172 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
))
1175 ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1179 /* We're done, break out */
1183 else if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOVALUE
)
1185 /* Just call the query routine */
1186 Status
= QueryTable
->QueryRoutine(NULL
,
1191 QueryTable
->EntryContext
);
1196 /* Loop every value */
1200 /* Enumerate the keys */
1201 Status
= ZwEnumerateValueKey(CurrentKey
,
1203 KeyValueFullInformation
,
1207 if (Status
== STATUS_BUFFER_OVERFLOW
)
1209 /* Normalize the status */
1210 Status
= STATUS_BUFFER_TOO_SMALL
;
1213 /* Check if we found all the entries */
1214 if (Status
== STATUS_NO_MORE_ENTRIES
)
1216 /* Check if this was the first entry and caller needs it */
1218 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
))
1221 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1225 /* Otherwise, it's ok */
1226 Status
= STATUS_SUCCESS
;
1231 /* Check if enumeration worked */
1232 if (NT_SUCCESS(Status
))
1234 /* Call the query routine */
1235 ResultLength
= InfoSize
;
1236 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1243 /* Check if the query failed */
1244 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1246 /* Increase allocation size */
1247 BufferSize
= ResultLength
+
1249 sizeof(UNICODE_NULL
);
1250 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1254 if (!KeyValueInfo
) break;
1256 /* Update the data */
1257 KeyValueInfo
->DataOffset
= 0;
1258 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1260 /* Try the value again unless it's been too many times */
1261 if (i
++ <= 4) continue;
1265 /* Break out if we failed */
1266 if (!NT_SUCCESS(Status
)) break;
1268 /* Reset the number of retries and check if we need to delete */
1270 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
)
1272 /* Build the name */
1273 RtlInitEmptyUnicodeString(&KeyValueName
,
1275 (USHORT
)KeyValueInfo
->NameLength
);
1276 KeyValueName
.Length
= KeyValueName
.MaximumLength
;
1278 /* Delete the key */
1279 Status
= ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1280 if (NT_SUCCESS(Status
)) Value
--;
1283 /* Go to the next value */
1288 /* Check if we failed anywhere along the road */
1289 if (!NT_SUCCESS(Status
)) break;
1295 /* Check if we need to close our handle */
1296 if ((KeyHandle
) && !(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1297 if ((CurrentKey
) && (CurrentKey
!= KeyHandle
)) ZwClose(CurrentKey
);
1299 /* Free our buffer and return status */
1300 RtlpAllocDeallocQueryBuffer(NULL
, KeyValueInfo
, BufferSize
, NULL
);