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
,
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
)
53 ActualLength
= MAXUSHORT
;
55 ActualLength
= (USHORT
)ValueLength
;
57 /* Check if the return string has been allocated */
58 if (!ReturnString
->Buffer
)
61 ReturnString
->Buffer
= RtlpAllocateMemory(ActualLength
, TAG_RTLREGISTRY
);
62 if (!ReturnString
->Buffer
) return STATUS_NO_MEMORY
;
63 ReturnString
->MaximumLength
= ActualLength
;
65 else if (ActualLength
> ReturnString
->MaximumLength
)
67 /* The string the caller allocated is too small */
68 return STATUS_BUFFER_TOO_SMALL
;
72 RtlCopyMemory(ReturnString
->Buffer
, ValueData
, ActualLength
);
73 ReturnString
->Length
= ActualLength
- sizeof(UNICODE_NULL
);
75 else if (ValueLength
<= sizeof(ULONG
))
77 /* Check if we can just copy the data */
78 if ((Buffer
!= ValueData
) && (ValueLength
))
81 RtlCopyMemory(Buffer
, ValueData
, ValueLength
);
86 /* Check if the length is negative */
87 if ((LONG
)*Length
< 0)
89 /* Get the real length and copy the buffer */
90 RealLength
= -(LONG
)*Length
;
91 if (RealLength
< ValueLength
) return STATUS_BUFFER_TOO_SMALL
;
92 RtlCopyMemory(Buffer
, ValueData
, ValueLength
);
96 /* Check if there's space for the length and type, plus data */
97 if (*Length
< (2 * sizeof(ULONG
) + ValueLength
))
100 return STATUS_BUFFER_TOO_SMALL
;
103 /* Return the data */
104 *Length
++ = ValueLength
;
105 *Length
++ = ValueType
;
106 RtlCopyMemory(Length
, ValueData
, ValueLength
);
111 return STATUS_SUCCESS
;
116 RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
117 IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo
,
118 IN OUT PULONG InfoSize
,
120 IN PVOID Environment
)
123 SIZE_T Length
, SpareLength
, c
;
124 ULONG RequiredLength
;
125 PCHAR SpareData
, DataEnd
;
127 PWCHAR Name
, p
, ValueEnd
;
130 BOOLEAN FoundExpander
= FALSE
;
131 UNICODE_STRING Source
, Destination
;
134 InfoLength
= *InfoSize
;
137 /* Check if there's no data */
138 if (KeyValueInfo
->DataOffset
== MAXULONG
)
140 /* Return proper status code */
141 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
142 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
145 /* Setup spare data pointers */
146 SpareData
= (PCHAR
)KeyValueInfo
;
147 SpareLength
= InfoLength
;
148 DataEnd
= SpareData
+ SpareLength
;
150 /* Check if there's no value or data */
151 if ((KeyValueInfo
->Type
== REG_NONE
) ||
152 (!(KeyValueInfo
->DataLength
) &&
153 (KeyValueInfo
->Type
== QueryTable
->DefaultType
)))
155 /* Check if there's no value */
156 if (QueryTable
->DefaultType
== REG_NONE
)
158 /* Return proper status code */
159 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
160 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
163 /* We can setup a default value... capture the defaults */
164 Name
= (PWCHAR
)QueryTable
->Name
;
165 Type
= QueryTable
->DefaultType
;
166 Data
= QueryTable
->DefaultData
;
167 Length
= QueryTable
->DefaultLength
;
170 /* No default length given, try to calculate it */
172 if ((Type
== REG_SZ
) || (Type
== REG_EXPAND_SZ
))
174 /* This is a string, count the characters */
176 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
178 else if (Type
== REG_MULTI_SZ
)
180 /* This is a multi-string, calculate all characters */
181 while (*p
) while (*p
++);
182 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
+ sizeof(UNICODE_NULL
);
188 /* Check if this isn't a direct return */
189 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
))
191 /* Check if we have length */
192 if (KeyValueInfo
->DataLength
)
194 /* Increase the spare data */
195 SpareData
+= KeyValueInfo
->DataOffset
+
196 KeyValueInfo
->DataLength
;
200 /* Otherwise, the spare data only has the name data */
201 SpareData
+= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
202 KeyValueInfo
->NameLength
;
205 /* Align the pointer and get new size of spare data */
206 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
207 SpareLength
= DataEnd
- SpareData
;
209 /* Check if we have space to copy the data */
210 RequiredLength
= KeyValueInfo
->NameLength
+ sizeof(UNICODE_NULL
);
211 if (SpareLength
< RequiredLength
)
213 /* Fail and return the missing length */
214 *InfoSize
= (ULONG
)(SpareData
- (PCHAR
)KeyValueInfo
) + RequiredLength
;
215 return STATUS_BUFFER_TOO_SMALL
;
218 /* Copy the data and null-terminate it */
219 Name
= (PWCHAR
)SpareData
;
220 RtlCopyMemory(Name
, KeyValueInfo
->Name
, KeyValueInfo
->NameLength
);
221 Name
[KeyValueInfo
->NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
223 /* Update the spare data information */
224 SpareData
+= RequiredLength
;
225 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
226 SpareLength
= DataEnd
- SpareData
;
230 /* Just return the name */
231 Name
= (PWCHAR
)QueryTable
->Name
;
234 /* Capture key data */
235 Type
= KeyValueInfo
->Type
;
236 Data
= (PVOID
)((ULONG_PTR
)KeyValueInfo
+ KeyValueInfo
->DataOffset
);
237 Length
= KeyValueInfo
->DataLength
;
240 /* Check if we're expanding */
241 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOEXPAND
))
243 /* Check if it's a multi-string */
244 if (Type
== REG_MULTI_SZ
)
246 /* Prepare defaults */
247 Status
= STATUS_SUCCESS
;
248 /* Skip the last two UNICODE_NULL chars (the terminating null string) */
249 ValueEnd
= (PWSTR
)((ULONG_PTR
)Data
+ Length
- 2 * sizeof(UNICODE_NULL
));
252 /* Loop all strings */
255 /* Go to the next string */
258 /* Get the length and check if this is direct */
259 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
260 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
263 Status
= RtlpQueryRegistryDirect(REG_SZ
,
266 QueryTable
->EntryContext
);
267 QueryTable
->EntryContext
=
268 (PVOID
)((ULONG_PTR
)QueryTable
->EntryContext
+
269 sizeof(UNICODE_STRING
));
273 /* Call the custom routine */
274 Status
= QueryTable
->QueryRoutine(Name
,
279 QueryTable
->EntryContext
);
282 /* Normalize status */
283 if (Status
== STATUS_BUFFER_TOO_SMALL
) Status
= STATUS_SUCCESS
;
284 if (!NT_SUCCESS(Status
)) break;
286 /* Update data pointer */
294 /* Check if this is an expand string */
295 if ((Type
== REG_EXPAND_SZ
) && (Length
>= sizeof(WCHAR
)))
297 /* Try to find the expander */
298 c
= Length
- sizeof(UNICODE_NULL
);
302 /* Check if this is one */
306 FoundExpander
= TRUE
;
310 /* Continue in the buffer */
315 /* So check if we have one */
318 /* Setup the source string */
319 RtlInitEmptyUnicodeString(&Source
, Data
, (USHORT
)Length
);
320 Source
.Length
= Source
.MaximumLength
- sizeof(UNICODE_NULL
);
322 /* Setup the desination string */
323 RtlInitEmptyUnicodeString(&Destination
, (PWCHAR
)SpareData
, 0);
325 /* Check if we're out of space */
326 if (SpareLength
<= 0)
328 /* Then we don't have any space in our string */
329 Destination
.MaximumLength
= 0;
331 else if (SpareLength
<= MAXUSHORT
)
333 /* This is the good case, where we fit into a string */
334 Destination
.MaximumLength
= (USHORT
)SpareLength
;
335 Destination
.Buffer
[SpareLength
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
339 /* We can't fit into a string, so truncate */
340 Destination
.MaximumLength
= MAXUSHORT
;
341 Destination
.Buffer
[MAXUSHORT
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
344 /* Expand the strings and set our type as one string */
345 Status
= RtlExpandEnvironmentStrings_U(Environment
,
351 /* Check for success */
352 if (NT_SUCCESS(Status
))
354 /* Set the value name and length to our string */
355 Data
= Destination
.Buffer
;
356 Length
= Destination
.Length
+ sizeof(UNICODE_NULL
);
360 /* Check if our buffer is too small */
361 if (Status
== STATUS_BUFFER_TOO_SMALL
)
363 /* Set the required missing length */
364 *InfoSize
= (ULONG
)(SpareData
- (PCHAR
)KeyValueInfo
) +
367 /* Notify debugger */
368 DPRINT1("RTL: Expand variables for %wZ failed - "
369 "Status == %lx Size %x > %x <%x>\n",
374 Destination
.MaximumLength
);
378 /* Notify debugger */
379 DPRINT1("RTL: Expand variables for %wZ failed - "
385 /* Return the status */
392 /* Check if this is a direct query */
393 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
395 /* Return the data */
396 Status
= RtlpQueryRegistryDirect(Type
,
399 QueryTable
->EntryContext
);
403 /* Call the query routine */
404 Status
= QueryTable
->QueryRoutine(Name
,
409 QueryTable
->EntryContext
);
412 /* Normalize and return status */
413 return (Status
== STATUS_BUFFER_TOO_SMALL
) ? STATUS_SUCCESS
: Status
;
416 _Success_(return!=NULL
|| BufferSize
==0)
417 _When_(BufferSize
!=NULL
,__drv_allocatesMem(Mem
))
420 RtlpAllocDeallocQueryBuffer(
421 _In_opt_ PSIZE_T BufferSize
,
422 _In_opt_
__drv_freesMem(Mem
) PVOID OldBuffer
,
423 _In_ SIZE_T OldBufferSize
,
424 _Out_opt_
_On_failure_(_Post_satisfies_(*Status
< 0)) PNTSTATUS Status
)
429 if (Status
) *Status
= STATUS_SUCCESS
;
431 /* Free the old buffer */
432 if (OldBuffer
) RtlpFreeMemory(OldBuffer
, TAG_RTLREGISTRY
);
434 /* Check if we need to allocate a new one */
438 Buffer
= RtlpAllocateMemory(*BufferSize
, TAG_RTLREGISTRY
);
439 if (!(Buffer
) && (Status
)) *Status
= STATUS_NO_MEMORY
;
442 /* Return the pointer */
448 RtlpGetRegistryHandle(IN ULONG RelativeTo
,
451 IN PHANDLE KeyHandle
)
453 UNICODE_STRING KeyPath
, KeyName
;
454 WCHAR KeyBuffer
[MAX_PATH
];
455 OBJECT_ATTRIBUTES ObjectAttributes
;
458 /* Check if we just want the handle */
459 if (RelativeTo
& RTL_REGISTRY_HANDLE
)
461 *KeyHandle
= (HANDLE
)Path
;
462 return STATUS_SUCCESS
;
465 /* Check for optional flag */
466 if (RelativeTo
& RTL_REGISTRY_OPTIONAL
)
469 RelativeTo
&= ~RTL_REGISTRY_OPTIONAL
;
472 /* Fail on invalid parameter */
473 if (RelativeTo
>= RTL_REGISTRY_MAXIMUM
) return STATUS_INVALID_PARAMETER
;
475 /* Initialize the key name */
476 RtlInitEmptyUnicodeString(&KeyName
, KeyBuffer
, sizeof(KeyBuffer
));
478 /* Check if we have to lookup a path to prefix */
479 if (RelativeTo
!= RTL_REGISTRY_ABSOLUTE
)
481 /* Check if we need the current user key */
482 if (RelativeTo
== RTL_REGISTRY_USER
)
484 /* Get the user key path */
485 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
487 /* Check if it worked */
488 if (NT_SUCCESS(Status
))
490 /* Append the user key path */
491 Status
= RtlAppendUnicodeStringToString(&KeyName
, &KeyPath
);
493 /* Free the user key path */
494 RtlFreeUnicodeString (&KeyPath
);
498 /* It didn't work so fall back to the default user key */
499 Status
= RtlAppendUnicodeToString(&KeyName
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
504 /* Get one of the prefixes */
505 Status
= RtlAppendUnicodeToString(&KeyName
,
506 RtlpRegPaths
[RelativeTo
]);
509 /* Check for failure, otherwise, append the path separator */
510 if (!NT_SUCCESS(Status
)) return Status
;
511 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
512 if (!NT_SUCCESS(Status
)) return Status
;
515 /* And now append the path */
516 if (Path
[0] == L
'\\' && RelativeTo
!= RTL_REGISTRY_ABSOLUTE
) Path
++; // HACK!
517 Status
= RtlAppendUnicodeToString(&KeyName
, Path
);
518 if (!NT_SUCCESS(Status
)) return Status
;
520 /* Initialize the object attributes */
521 InitializeObjectAttributes(&ObjectAttributes
,
523 OBJ_CASE_INSENSITIVE
,
527 /* Check if we want to create it */
530 /* Create the key with write privileges */
531 Status
= ZwCreateKey(KeyHandle
,
541 /* Otherwise, just open it with read access */
542 Status
= ZwOpenKey(KeyHandle
,
543 MAXIMUM_ALLOWED
| GENERIC_READ
,
551 /* PUBLIC FUNCTIONS **********************************************************/
558 RtlCheckRegistryKey(IN ULONG RelativeTo
,
565 /* Call the helper */
566 Status
= RtlpGetRegistryHandle(RelativeTo
,
570 if (!NT_SUCCESS(Status
)) return Status
;
572 /* All went well, close the handle and return success */
574 return STATUS_SUCCESS
;
582 RtlCreateRegistryKey(IN ULONG RelativeTo
,
589 /* Call the helper */
590 Status
= RtlpGetRegistryHandle(RelativeTo
,
594 if (!NT_SUCCESS(Status
)) return Status
;
596 /* All went well, close the handle and return success */
598 return STATUS_SUCCESS
;
606 RtlDeleteRegistryValue(IN ULONG RelativeTo
,
615 /* Call the helper */
616 Status
= RtlpGetRegistryHandle(RelativeTo
,
620 if (!NT_SUCCESS(Status
)) return Status
;
622 /* Initialize the key name and delete it */
623 RtlInitUnicodeString(&Name
, ValueName
);
624 Status
= ZwDeleteValueKey(KeyHandle
, &Name
);
626 /* All went well, close the handle and return status */
636 RtlWriteRegistryValue(IN ULONG RelativeTo
,
641 IN ULONG ValueLength
)
648 /* Call the helper */
649 Status
= RtlpGetRegistryHandle(RelativeTo
,
653 if (!NT_SUCCESS(Status
)) return Status
;
655 /* Initialize the key name and set it */
656 RtlInitUnicodeString(&Name
, ValueName
);
657 Status
= ZwSetValueKey(KeyHandle
,
664 /* Did the caller pass a key handle? */
665 if (!(RelativeTo
& RTL_REGISTRY_HANDLE
))
667 /* We opened the key in RtlpGetRegistryHandle, so close it now */
679 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess
,
680 OUT PHANDLE KeyHandle
)
682 OBJECT_ATTRIBUTES ObjectAttributes
;
683 UNICODE_STRING KeyPath
;
687 /* Get the user key */
688 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
689 if (NT_SUCCESS(Status
))
691 /* Initialize the attributes and open it */
692 InitializeObjectAttributes(&ObjectAttributes
,
694 OBJ_CASE_INSENSITIVE
,
697 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
699 /* Free the path and return success if it worked */
700 RtlFreeUnicodeString(&KeyPath
);
701 if (NT_SUCCESS(Status
)) return STATUS_SUCCESS
;
704 /* It didn't work, so use the default key */
705 RtlInitUnicodeString(&KeyPath
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
706 InitializeObjectAttributes(&ObjectAttributes
,
708 OBJ_CASE_INSENSITIVE
,
711 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
722 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath
)
726 PSID_AND_ATTRIBUTES SidBuffer
;
728 UNICODE_STRING SidString
;
732 /* Open the thread token */
733 Status
= ZwOpenThreadToken(NtCurrentThread(),
737 if (!NT_SUCCESS(Status
))
739 /* We failed, is it because we don't have a thread token? */
740 if (Status
!= STATUS_NO_TOKEN
) return Status
;
742 /* It is, so use the process token */
743 Status
= ZwOpenProcessToken(NtCurrentProcess(),
746 if (!NT_SUCCESS(Status
)) return Status
;
749 /* Now query the token information */
750 SidBuffer
= (PSID_AND_ATTRIBUTES
)Buffer
;
751 Status
= ZwQueryInformationToken(TokenHandle
,
757 /* Close the handle and handle failure */
758 ZwClose(TokenHandle
);
759 if (!NT_SUCCESS(Status
)) return Status
;
761 /* Convert the SID */
762 Status
= RtlConvertSidToUnicodeString(&SidString
, SidBuffer
[0].Sid
, TRUE
);
763 if (!NT_SUCCESS(Status
)) return Status
;
765 /* Add the length of the prefix */
766 Length
= SidString
.Length
+ sizeof(L
"\\REGISTRY\\USER\\");
768 /* Initialize a string */
769 RtlInitEmptyUnicodeString(KeyPath
,
770 RtlpAllocateStringMemory(Length
, TAG_USTR
),
772 if (!KeyPath
->Buffer
)
774 /* Free the string and fail */
775 RtlFreeUnicodeString(&SidString
);
776 return STATUS_NO_MEMORY
;
779 /* Append the prefix and SID */
780 RtlAppendUnicodeToString(KeyPath
, L
"\\REGISTRY\\USER\\");
781 RtlAppendUnicodeStringToString(KeyPath
, &SidString
);
783 /* Free the temporary string and return success */
784 RtlFreeUnicodeString(&SidString
);
785 return STATUS_SUCCESS
;
793 RtlpNtCreateKey(OUT HANDLE KeyHandle
,
794 IN ACCESS_MASK DesiredAccess
,
795 IN POBJECT_ATTRIBUTES ObjectAttributes
,
797 IN PUNICODE_STRING Class
,
798 OUT PULONG Disposition
)
800 /* Check if we have object attributes */
801 if (ObjectAttributes
)
803 /* Mask out the unsupported flags */
804 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
808 return ZwCreateKey(KeyHandle
,
822 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle
,
823 OUT PUNICODE_STRING SubKeyName
,
827 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
828 ULONG BufferLength
= 0;
829 ULONG ReturnedLength
;
832 /* Check if we have a name */
833 if (SubKeyName
->MaximumLength
)
835 /* Allocate a buffer for it */
836 BufferLength
= SubKeyName
->MaximumLength
+
837 sizeof(KEY_BASIC_INFORMATION
);
838 KeyInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
839 if (!KeyInfo
) return STATUS_NO_MEMORY
;
842 /* Enumerate the key */
843 Status
= ZwEnumerateKey(KeyHandle
,
849 if (NT_SUCCESS(Status
) && (KeyInfo
!= NULL
))
851 /* Check if the name fits */
852 if (KeyInfo
->NameLength
<= SubKeyName
->MaximumLength
)
855 SubKeyName
->Length
= (USHORT
)KeyInfo
->NameLength
;
858 RtlMoveMemory(SubKeyName
->Buffer
,
864 /* Otherwise, we ran out of buffer space */
865 Status
= STATUS_BUFFER_OVERFLOW
;
869 /* Free the buffer and return status */
870 if (KeyInfo
) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo
);
879 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle
)
881 /* This just deletes the key */
882 return ZwDeleteKey(KeyHandle
);
890 RtlpNtOpenKey(OUT HANDLE KeyHandle
,
891 IN ACCESS_MASK DesiredAccess
,
892 IN POBJECT_ATTRIBUTES ObjectAttributes
,
895 /* Check if we have object attributes */
896 if (ObjectAttributes
)
898 /* Mask out the unsupported flags */
899 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
903 return ZwOpenKey(KeyHandle
, DesiredAccess
, ObjectAttributes
);
911 RtlpNtQueryValueKey(IN HANDLE KeyHandle
,
912 OUT PULONG Type OPTIONAL
,
913 OUT PVOID Data OPTIONAL
,
914 IN OUT PULONG DataLength OPTIONAL
,
917 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
918 UNICODE_STRING ValueName
;
919 ULONG BufferLength
= 0;
922 /* Clear the value name */
923 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
925 /* Check if we were already given a length */
926 if (DataLength
) BufferLength
= *DataLength
;
928 /* Add the size of the structure */
929 BufferLength
+= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
931 /* Allocate memory for the value */
932 ValueInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
933 if (!ValueInfo
) return STATUS_NO_MEMORY
;
935 /* Query the value */
936 Status
= ZwQueryValueKey(KeyHandle
,
938 KeyValuePartialInformation
,
942 if ((NT_SUCCESS(Status
)) || (Status
== STATUS_BUFFER_OVERFLOW
))
944 /* Return the length and type */
945 if (DataLength
) *DataLength
= ValueInfo
->DataLength
;
946 if (Type
) *Type
= ValueInfo
->Type
;
949 /* Check if the caller wanted data back, and we got it */
950 if ((NT_SUCCESS(Status
)) && (Data
))
953 RtlMoveMemory(Data
, ValueInfo
->Data
, ValueInfo
->DataLength
);
956 /* Free the memory and return status */
957 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo
);
966 RtlpNtSetValueKey(IN HANDLE KeyHandle
,
971 UNICODE_STRING ValueName
;
974 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
975 return ZwSetValueKey(KeyHandle
,
988 RtlQueryRegistryValues(IN ULONG RelativeTo
,
990 IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
992 IN PVOID Environment OPTIONAL
)
995 PKEY_VALUE_FULL_INFORMATION KeyValueInfo
= NULL
;
996 HANDLE KeyHandle
, CurrentKey
;
997 SIZE_T BufferSize
, InfoSize
;
998 UNICODE_STRING KeyPath
, KeyValueName
;
999 OBJECT_ATTRIBUTES ObjectAttributes
;
1003 /* Get the registry handle */
1004 Status
= RtlpGetRegistryHandle(RelativeTo
, Path
, FALSE
, &KeyHandle
);
1005 if (!NT_SUCCESS(Status
)) return Status
;
1007 /* Initialize the path */
1008 RtlInitUnicodeString(&KeyPath
,
1009 (RelativeTo
& RTL_REGISTRY_HANDLE
) ? NULL
: Path
);
1011 /* Allocate a query buffer */
1012 BufferSize
= RtlpAllocDeallocQueryBufferSize
;
1013 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
, NULL
, 0, &Status
);
1016 /* Close the handle if we have one and fail */
1017 if (!(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1022 KeyValueInfo
->DataOffset
= 0;
1023 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1024 CurrentKey
= KeyHandle
;
1026 /* Loop the query table */
1027 while ((QueryTable
->QueryRoutine
) ||
1028 (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_SUBKEY
|
1029 RTL_QUERY_REGISTRY_DIRECT
)))
1031 /* Check if the request is invalid */
1032 if ((QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
) &&
1033 (!(QueryTable
->Name
) ||
1034 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
) ||
1035 (QueryTable
->QueryRoutine
)))
1038 Status
= STATUS_INVALID_PARAMETER
;
1042 /* Check if we want a specific key */
1043 if (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_TOPKEY
|
1044 RTL_QUERY_REGISTRY_SUBKEY
))
1046 /* Check if we're working with another handle */
1047 if (CurrentKey
!= KeyHandle
)
1049 /* Close our current key and use the top */
1050 NtClose(CurrentKey
);
1051 CurrentKey
= KeyHandle
;
1055 /* Check if we're querying the subkey */
1056 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
)
1058 /* Make sure we have a name */
1059 if (!QueryTable
->Name
)
1062 Status
= STATUS_INVALID_PARAMETER
;
1066 /* Initialize the name */
1067 RtlInitUnicodeString(&KeyPath
, QueryTable
->Name
);
1069 /* Get the key handle */
1070 InitializeObjectAttributes(&ObjectAttributes
,
1072 OBJ_CASE_INSENSITIVE
|
1076 Status
= ZwOpenKey(&CurrentKey
,
1079 if (NT_SUCCESS(Status
))
1081 /* If we have a query routine, go enumerate values */
1082 if (QueryTable
->QueryRoutine
) goto ProcessValues
;
1086 else if (QueryTable
->Name
)
1088 /* Initialize the path */
1089 RtlInitUnicodeString(&KeyValueName
, QueryTable
->Name
);
1091 /* Start query loop */
1095 /* Make sure we didn't retry too many times */
1099 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1100 "at line %d\n", __LINE__
);
1104 /* Query key information */
1105 Status
= ZwQueryValueKey(CurrentKey
,
1107 KeyValueFullInformation
,
1111 if (Status
== STATUS_BUFFER_OVERFLOW
)
1113 /* Normalize status code */
1114 Status
= STATUS_BUFFER_TOO_SMALL
;
1117 /* Check for failure */
1118 if (!NT_SUCCESS(Status
))
1120 /* Check if we didn't find it */
1121 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1123 /* Setup a default */
1124 KeyValueInfo
->Type
= REG_NONE
;
1125 KeyValueInfo
->DataLength
= 0;
1126 ResultLength
= (ULONG
)InfoSize
;
1128 /* Call the query routine */
1129 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1136 /* Check for buffer being too small */
1137 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1139 /* Increase allocation size */
1140 BufferSize
= ResultLength
+
1142 sizeof(UNICODE_NULL
);
1143 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1147 if (!KeyValueInfo
) break;
1149 /* Update the data */
1150 KeyValueInfo
->DataOffset
= 0;
1151 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1157 /* Check if this is a multi-string */
1158 if (KeyValueInfo
->Type
== REG_MULTI_SZ
)
1160 /* Add a null-char */
1161 ((PWCHAR
)KeyValueInfo
)[ResultLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1162 KeyValueInfo
->DataLength
+= sizeof(UNICODE_NULL
);
1165 /* Call the query routine */
1166 ResultLength
= (ULONG
)InfoSize
;
1167 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1173 /* Check for buffer being too small */
1174 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1176 /* Increase allocation size */
1177 BufferSize
= ResultLength
+
1179 sizeof(UNICODE_NULL
);
1180 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1184 if (!KeyValueInfo
) break;
1186 /* Update the data */
1187 KeyValueInfo
->DataOffset
= 0;
1188 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1192 /* Check if we need to delete the key */
1193 if ((NT_SUCCESS(Status
)) &&
1194 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
))
1197 ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1201 /* We're done, break out */
1205 else if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOVALUE
)
1207 /* Just call the query routine */
1208 Status
= QueryTable
->QueryRoutine(NULL
,
1213 QueryTable
->EntryContext
);
1218 /* Loop every value */
1222 /* Enumerate the keys */
1223 Status
= ZwEnumerateValueKey(CurrentKey
,
1225 KeyValueFullInformation
,
1229 if (Status
== STATUS_BUFFER_OVERFLOW
)
1231 /* Normalize the status */
1232 Status
= STATUS_BUFFER_TOO_SMALL
;
1235 /* Check if we found all the entries */
1236 if (Status
== STATUS_NO_MORE_ENTRIES
)
1238 /* Check if this was the first entry and caller needs it */
1240 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
))
1243 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1247 /* Otherwise, it's ok */
1248 Status
= STATUS_SUCCESS
;
1253 /* Check if enumeration worked */
1254 if (NT_SUCCESS(Status
))
1256 /* Call the query routine */
1257 ResultLength
= (ULONG
)InfoSize
;
1258 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1265 /* Check if the query failed */
1266 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1268 /* Increase allocation size */
1269 BufferSize
= ResultLength
+
1271 sizeof(UNICODE_NULL
);
1272 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1276 if (!KeyValueInfo
) break;
1278 /* Update the data */
1279 KeyValueInfo
->DataOffset
= 0;
1280 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1282 /* Try the value again unless it's been too many times */
1283 if (i
++ <= 4) continue;
1287 /* Break out if we failed */
1288 if (!NT_SUCCESS(Status
)) break;
1290 /* Reset the number of retries and check if we need to delete */
1292 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
)
1294 /* Build the name */
1295 RtlInitEmptyUnicodeString(&KeyValueName
,
1297 (USHORT
)KeyValueInfo
->NameLength
);
1298 KeyValueName
.Length
= KeyValueName
.MaximumLength
;
1300 /* Delete the key */
1301 Status
= ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1302 if (NT_SUCCESS(Status
)) Value
--;
1305 /* Go to the next value */
1310 /* Check if we failed anywhere along the road */
1311 if (!NT_SUCCESS(Status
)) break;
1317 /* Check if we need to close our handle */
1318 if ((KeyHandle
) && !(RelativeTo
& RTL_REGISTRY_HANDLE
)) ZwClose(KeyHandle
);
1319 if ((CurrentKey
) && (CurrentKey
!= KeyHandle
)) ZwClose(CurrentKey
);
1321 /* Free our buffer and return status */
1322 RtlpAllocDeallocQueryBuffer(NULL
, KeyValueInfo
, BufferSize
, NULL
);