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 *****************************************************************/
14 #include <ndk/cmfuncs.h>
19 #define TAG_RTLREGISTRY 'vrqR'
21 extern SIZE_T RtlpAllocDeallocQueryBufferSize
;
23 /* DATA **********************************************************************/
25 PCWSTR RtlpRegPaths
[RTL_REGISTRY_MAXIMUM
] =
28 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services",
29 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
30 L
"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
31 L
"\\Registry\\Machine\\Hardware\\DeviceMap",
32 L
"\\Registry\\User\\.Default",
35 /* PRIVATE FUNCTIONS *********************************************************/
39 RtlpQueryRegistryDirect(IN ULONG ValueType
,
45 PUNICODE_STRING ReturnString
= Buffer
;
46 PULONG Length
= Buffer
;
49 /* Check if this is a string */
50 if ((ValueType
== REG_SZ
) ||
51 (ValueType
== REG_EXPAND_SZ
) ||
52 (ValueType
== REG_MULTI_SZ
))
54 /* Normalize the length */
55 if (ValueLength
> MAXUSHORT
)
56 ActualLength
= MAXUSHORT
;
58 ActualLength
= (USHORT
)ValueLength
;
60 /* Check if the return string has been allocated */
61 if (!ReturnString
->Buffer
)
64 ReturnString
->Buffer
= RtlpAllocateMemory(ActualLength
, TAG_RTLREGISTRY
);
65 if (!ReturnString
->Buffer
) return STATUS_NO_MEMORY
;
66 ReturnString
->MaximumLength
= ActualLength
;
68 else if (ActualLength
> ReturnString
->MaximumLength
)
70 /* The string the caller allocated is too small */
71 return STATUS_BUFFER_TOO_SMALL
;
75 RtlCopyMemory(ReturnString
->Buffer
, ValueData
, ActualLength
);
76 ReturnString
->Length
= ActualLength
- sizeof(UNICODE_NULL
);
78 else if (ValueLength
<= sizeof(ULONG
))
80 /* Check if we can just copy the data */
81 if ((Buffer
!= ValueData
) && (ValueLength
))
84 RtlCopyMemory(Buffer
, ValueData
, ValueLength
);
89 /* Check if the length is negative */
90 if ((LONG
)*Length
< 0)
92 /* Get the real length and copy the buffer */
93 RealLength
= -(LONG
)*Length
;
94 if (RealLength
< ValueLength
) return STATUS_BUFFER_TOO_SMALL
;
95 RtlCopyMemory(Buffer
, ValueData
, ValueLength
);
99 /* Check if there's space for the length and type, plus data */
100 if (*Length
< (2 * sizeof(ULONG
) + ValueLength
))
103 return STATUS_BUFFER_TOO_SMALL
;
106 /* Return the data */
107 *Length
++ = ValueLength
;
108 *Length
++ = ValueType
;
109 RtlCopyMemory(Length
, ValueData
, ValueLength
);
114 return STATUS_SUCCESS
;
119 RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
120 IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo
,
121 IN OUT PULONG InfoSize
,
123 IN PVOID Environment
)
126 SIZE_T Length
, SpareLength
, c
;
127 ULONG RequiredLength
;
128 PCHAR SpareData
, DataEnd
;
130 PWCHAR Name
, p
, ValueEnd
;
133 BOOLEAN FoundExpander
= FALSE
;
134 UNICODE_STRING Source
, Destination
;
137 InfoLength
= *InfoSize
;
140 /* Check if there's no data */
141 if (KeyValueInfo
->DataOffset
== MAXULONG
)
143 /* Return proper status code */
144 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
145 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
148 /* Setup spare data pointers */
149 SpareData
= (PCHAR
)KeyValueInfo
;
150 SpareLength
= InfoLength
;
151 DataEnd
= SpareData
+ SpareLength
;
153 /* Check if there's no value or data */
154 if ((KeyValueInfo
->Type
== REG_NONE
) ||
155 (!(KeyValueInfo
->DataLength
) &&
156 (KeyValueInfo
->Type
== QueryTable
->DefaultType
)))
158 /* Check if there's no value */
159 if (QueryTable
->DefaultType
== REG_NONE
)
161 /* Return proper status code */
162 return (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
) ?
163 STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_SUCCESS
;
166 /* We can setup a default value... capture the defaults */
167 Name
= (PWCHAR
)QueryTable
->Name
;
168 Type
= QueryTable
->DefaultType
;
169 Data
= QueryTable
->DefaultData
;
170 Length
= QueryTable
->DefaultLength
;
173 /* No default length given, try to calculate it */
175 if ((Type
== REG_SZ
) || (Type
== REG_EXPAND_SZ
))
177 /* This is a string, count the characters */
179 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
181 else if (Type
== REG_MULTI_SZ
)
183 /* This is a multi-string, calculate all characters */
184 while (*p
) while (*p
++);
185 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
+ sizeof(UNICODE_NULL
);
191 /* Check if this isn't a direct return */
192 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
))
194 /* Check if we have length */
195 if (KeyValueInfo
->DataLength
)
197 /* Increase the spare data */
198 SpareData
+= KeyValueInfo
->DataOffset
+
199 KeyValueInfo
->DataLength
;
203 /* Otherwise, the spare data only has the name data */
204 SpareData
+= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
205 KeyValueInfo
->NameLength
;
208 /* Align the pointer and get new size of spare data */
209 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
210 SpareLength
= DataEnd
- SpareData
;
212 /* Check if we have space to copy the data */
213 RequiredLength
= KeyValueInfo
->NameLength
+ sizeof(UNICODE_NULL
);
214 if ((SpareData
> DataEnd
) || (SpareLength
< RequiredLength
))
216 /* Fail and return the missing length */
217 *InfoSize
= (ULONG
)(SpareData
- (PCHAR
)KeyValueInfo
) + RequiredLength
;
218 return STATUS_BUFFER_TOO_SMALL
;
221 /* Copy the data and null-terminate it */
222 Name
= (PWCHAR
)SpareData
;
223 RtlCopyMemory(Name
, KeyValueInfo
->Name
, KeyValueInfo
->NameLength
);
224 Name
[KeyValueInfo
->NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
226 /* Update the spare data information */
227 SpareData
+= RequiredLength
;
228 SpareData
= (PVOID
)(((ULONG_PTR
)SpareData
+ 7) & ~7);
229 SpareLength
= DataEnd
- SpareData
;
233 /* Just return the name */
234 Name
= (PWCHAR
)QueryTable
->Name
;
237 /* Capture key data */
238 Type
= KeyValueInfo
->Type
;
239 Data
= (PVOID
)((ULONG_PTR
)KeyValueInfo
+ KeyValueInfo
->DataOffset
);
240 Length
= KeyValueInfo
->DataLength
;
243 /* Check if we're expanding */
244 if (!(QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOEXPAND
))
246 /* Check if it's a multi-string */
247 if (Type
== REG_MULTI_SZ
)
249 /* Prepare defaults */
250 Status
= STATUS_SUCCESS
;
251 /* Skip the last two UNICODE_NULL chars (the terminating null string) */
252 ValueEnd
= (PWSTR
)((ULONG_PTR
)Data
+ Length
- 2 * sizeof(UNICODE_NULL
));
255 /* Loop all strings */
258 /* Go to the next string */
261 /* Get the length and check if this is direct */
262 Length
= (ULONG_PTR
)p
- (ULONG_PTR
)Data
;
263 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
266 Status
= RtlpQueryRegistryDirect(REG_SZ
,
269 QueryTable
->EntryContext
);
270 QueryTable
->EntryContext
=
271 (PVOID
)((ULONG_PTR
)QueryTable
->EntryContext
+
272 sizeof(UNICODE_STRING
));
276 /* Call the custom routine */
277 Status
= QueryTable
->QueryRoutine(Name
,
282 QueryTable
->EntryContext
);
285 /* Normalize status */
286 if (Status
== STATUS_BUFFER_TOO_SMALL
) Status
= STATUS_SUCCESS
;
287 if (!NT_SUCCESS(Status
)) break;
289 /* Update data pointer */
297 /* Check if this is an expand string */
298 if ((Type
== REG_EXPAND_SZ
) && (Length
>= sizeof(WCHAR
)))
300 /* Try to find the expander */
301 c
= Length
- sizeof(UNICODE_NULL
);
305 /* Check if this is one */
309 FoundExpander
= TRUE
;
313 /* Continue in the buffer */
318 /* So check if we have one */
321 /* Setup the source string */
322 RtlInitEmptyUnicodeString(&Source
, Data
, (USHORT
)Length
);
323 Source
.Length
= Source
.MaximumLength
- sizeof(UNICODE_NULL
);
325 /* Setup the desination string */
326 RtlInitEmptyUnicodeString(&Destination
, (PWCHAR
)SpareData
, 0);
328 /* Check if we're out of space */
329 if (SpareLength
<= 0)
331 /* Then we don't have any space in our string */
332 Destination
.MaximumLength
= 0;
334 else if (SpareLength
<= MAXUSHORT
)
336 /* This is the good case, where we fit into a string */
337 Destination
.MaximumLength
= (USHORT
)SpareLength
;
338 Destination
.Buffer
[SpareLength
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
342 /* We can't fit into a string, so truncate */
343 Destination
.MaximumLength
= MAXUSHORT
;
344 Destination
.Buffer
[MAXUSHORT
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
347 /* Expand the strings and set our type as one string */
348 Status
= RtlExpandEnvironmentStrings_U(Environment
,
354 /* Check for success */
355 if (NT_SUCCESS(Status
))
357 /* Set the value name and length to our string */
358 Data
= Destination
.Buffer
;
359 Length
= Destination
.Length
+ sizeof(UNICODE_NULL
);
363 /* Check if our buffer is too small */
364 if (Status
== STATUS_BUFFER_TOO_SMALL
)
366 /* Set the required missing length */
367 *InfoSize
= (ULONG
)(SpareData
- (PCHAR
)KeyValueInfo
) +
370 /* Notify debugger */
371 DPRINT1("RTL: Expand variables for %wZ failed - "
372 "Status == %lx Size %x > %x <%x>\n",
377 Destination
.MaximumLength
);
381 /* Notify debugger */
382 DPRINT1("RTL: Expand variables for %wZ failed - "
388 /* Return the status */
395 /* Check if this is a direct query */
396 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
)
398 /* Return the data */
399 Status
= RtlpQueryRegistryDirect(Type
,
402 QueryTable
->EntryContext
);
406 /* Call the query routine */
407 Status
= QueryTable
->QueryRoutine(Name
,
412 QueryTable
->EntryContext
);
415 /* Normalize and return status */
416 return (Status
== STATUS_BUFFER_TOO_SMALL
) ? STATUS_SUCCESS
: Status
;
419 _Success_(return!=NULL
|| BufferSize
==0)
420 _When_(BufferSize
!=NULL
,__drv_allocatesMem(Mem
))
423 RtlpAllocDeallocQueryBuffer(
424 _In_opt_ PSIZE_T BufferSize
,
425 _In_opt_
__drv_freesMem(Mem
) PVOID OldBuffer
,
426 _In_ SIZE_T OldBufferSize
,
427 _Out_opt_
_On_failure_(_Post_satisfies_(*Status
< 0)) PNTSTATUS Status
)
432 if (Status
) *Status
= STATUS_SUCCESS
;
434 /* Free the old buffer */
435 if (OldBuffer
) RtlpFreeMemory(OldBuffer
, TAG_RTLREGISTRY
);
437 /* Check if we need to allocate a new one */
441 Buffer
= RtlpAllocateMemory(*BufferSize
, TAG_RTLREGISTRY
);
442 if (!(Buffer
) && (Status
)) *Status
= STATUS_NO_MEMORY
;
445 /* Return the pointer */
451 RtlpGetRegistryHandle(IN ULONG RelativeTo
,
454 IN PHANDLE KeyHandle
)
456 UNICODE_STRING KeyPath
, KeyName
;
457 WCHAR KeyBuffer
[MAX_PATH
];
458 OBJECT_ATTRIBUTES ObjectAttributes
;
461 /* Check if we just want the handle */
462 if (RelativeTo
& RTL_REGISTRY_HANDLE
)
464 *KeyHandle
= (HANDLE
)Path
;
465 return STATUS_SUCCESS
;
468 /* Check for optional flag */
469 if (RelativeTo
& RTL_REGISTRY_OPTIONAL
)
472 RelativeTo
&= ~RTL_REGISTRY_OPTIONAL
;
475 /* Fail on invalid parameter */
476 if (RelativeTo
>= RTL_REGISTRY_MAXIMUM
) return STATUS_INVALID_PARAMETER
;
478 /* Initialize the key name */
479 RtlInitEmptyUnicodeString(&KeyName
, KeyBuffer
, sizeof(KeyBuffer
));
481 /* Check if we have to lookup a path to prefix */
482 if (RelativeTo
!= RTL_REGISTRY_ABSOLUTE
)
484 /* Check if we need the current user key */
485 if (RelativeTo
== RTL_REGISTRY_USER
)
487 /* Get the user key path */
488 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
490 /* Check if it worked */
491 if (NT_SUCCESS(Status
))
493 /* Append the user key path */
494 Status
= RtlAppendUnicodeStringToString(&KeyName
, &KeyPath
);
496 /* Free the user key path */
497 RtlFreeUnicodeString (&KeyPath
);
501 /* It didn't work so fall back to the default user key */
502 Status
= RtlAppendUnicodeToString(&KeyName
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
507 /* Get one of the prefixes */
508 Status
= RtlAppendUnicodeToString(&KeyName
,
509 RtlpRegPaths
[RelativeTo
]);
512 /* Check for failure, otherwise, append the path separator */
513 if (!NT_SUCCESS(Status
)) return Status
;
514 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
515 if (!NT_SUCCESS(Status
)) return Status
;
518 /* And now append the path */
519 if (Path
[0] == L
'\\' && RelativeTo
!= RTL_REGISTRY_ABSOLUTE
) Path
++; // HACK!
520 Status
= RtlAppendUnicodeToString(&KeyName
, Path
);
521 if (!NT_SUCCESS(Status
)) return Status
;
523 /* Initialize the object attributes */
524 InitializeObjectAttributes(&ObjectAttributes
,
526 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
530 /* Check if we want to create it */
533 /* Create the key with write privileges */
534 Status
= ZwCreateKey(KeyHandle
,
544 /* Otherwise, just open it with read access */
545 Status
= ZwOpenKey(KeyHandle
,
546 MAXIMUM_ALLOWED
| GENERIC_READ
,
556 RtlpCloseRegistryHandle(
557 _In_ ULONG RelativeTo
,
558 _In_ HANDLE KeyHandle
)
560 /* Did the caller pass a key handle? */
561 if (!(RelativeTo
& RTL_REGISTRY_HANDLE
))
563 /* We opened the key in RtlpGetRegistryHandle, so close it now */
568 /* PUBLIC FUNCTIONS **********************************************************/
575 RtlCheckRegistryKey(IN ULONG RelativeTo
,
582 /* Call the helper */
583 Status
= RtlpGetRegistryHandle(RelativeTo
,
587 if (!NT_SUCCESS(Status
)) return Status
;
589 /* Close the handle even for RTL_REGISTRY_HANDLE */
591 return STATUS_SUCCESS
;
599 RtlCreateRegistryKey(IN ULONG RelativeTo
,
606 /* Call the helper */
607 Status
= RtlpGetRegistryHandle(RelativeTo
,
611 if (!NT_SUCCESS(Status
)) return Status
;
613 /* All went well, close the handle and return status */
614 RtlpCloseRegistryHandle(RelativeTo
, KeyHandle
);
615 return STATUS_SUCCESS
;
623 RtlDeleteRegistryValue(IN ULONG RelativeTo
,
632 /* Call the helper */
633 Status
= RtlpGetRegistryHandle(RelativeTo
,
637 if (!NT_SUCCESS(Status
)) return Status
;
639 /* Initialize the key name and delete it */
640 RtlInitUnicodeString(&Name
, ValueName
);
641 Status
= ZwDeleteValueKey(KeyHandle
, &Name
);
643 /* Close the handle and return status */
644 RtlpCloseRegistryHandle(RelativeTo
, KeyHandle
);
653 RtlWriteRegistryValue(IN ULONG RelativeTo
,
658 IN ULONG ValueLength
)
665 /* Call the helper */
666 Status
= RtlpGetRegistryHandle(RelativeTo
,
670 if (!NT_SUCCESS(Status
)) return Status
;
672 /* Initialize the key name and set it */
673 RtlInitUnicodeString(&Name
, ValueName
);
674 Status
= ZwSetValueKey(KeyHandle
,
681 /* Close the handle and return status */
682 RtlpCloseRegistryHandle(RelativeTo
, KeyHandle
);
691 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess
,
692 OUT PHANDLE KeyHandle
)
694 OBJECT_ATTRIBUTES ObjectAttributes
;
695 UNICODE_STRING KeyPath
;
699 /* Get the user key */
700 Status
= RtlFormatCurrentUserKeyPath(&KeyPath
);
701 if (NT_SUCCESS(Status
))
703 /* Initialize the attributes and open it */
704 InitializeObjectAttributes(&ObjectAttributes
,
706 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
709 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
711 /* Free the path and return success if it worked */
712 RtlFreeUnicodeString(&KeyPath
);
713 if (NT_SUCCESS(Status
)) return STATUS_SUCCESS
;
716 /* It didn't work, so use the default key */
717 RtlInitUnicodeString(&KeyPath
, RtlpRegPaths
[RTL_REGISTRY_USER
]);
718 InitializeObjectAttributes(&ObjectAttributes
,
720 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
723 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
734 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath
)
738 PSID_AND_ATTRIBUTES SidBuffer
;
740 UNICODE_STRING SidString
;
744 /* Open the thread token */
745 Status
= ZwOpenThreadTokenEx(NtCurrentThread(),
750 if (!NT_SUCCESS(Status
))
752 /* We failed, is it because we don't have a thread token? */
753 if (Status
!= STATUS_NO_TOKEN
) return Status
;
755 /* It is, so use the process token */
756 Status
= ZwOpenProcessTokenEx(NtCurrentProcess(),
760 if (!NT_SUCCESS(Status
)) return Status
;
763 /* Now query the token information */
764 SidBuffer
= (PSID_AND_ATTRIBUTES
)Buffer
;
765 Status
= ZwQueryInformationToken(TokenHandle
,
771 /* Close the handle and handle failure */
772 ZwClose(TokenHandle
);
773 if (!NT_SUCCESS(Status
)) return Status
;
775 /* Convert the SID */
776 Status
= RtlConvertSidToUnicodeString(&SidString
, SidBuffer
[0].Sid
, TRUE
);
777 if (!NT_SUCCESS(Status
)) return Status
;
779 /* Add the length of the prefix */
780 Length
= SidString
.Length
+ sizeof(L
"\\REGISTRY\\USER\\");
782 /* Initialize a string */
783 RtlInitEmptyUnicodeString(KeyPath
,
784 RtlpAllocateStringMemory(Length
, TAG_USTR
),
786 if (!KeyPath
->Buffer
)
788 /* Free the string and fail */
789 RtlFreeUnicodeString(&SidString
);
790 return STATUS_NO_MEMORY
;
793 /* Append the prefix and SID */
794 RtlAppendUnicodeToString(KeyPath
, L
"\\REGISTRY\\USER\\");
795 RtlAppendUnicodeStringToString(KeyPath
, &SidString
);
797 /* Free the temporary string and return success */
798 RtlFreeUnicodeString(&SidString
);
799 return STATUS_SUCCESS
;
807 RtlpNtCreateKey(OUT HANDLE KeyHandle
,
808 IN ACCESS_MASK DesiredAccess
,
809 IN POBJECT_ATTRIBUTES ObjectAttributes
,
811 IN PUNICODE_STRING Class
,
812 OUT PULONG Disposition
)
814 /* Check if we have object attributes */
815 if (ObjectAttributes
)
817 /* Mask out the unsupported flags */
818 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
822 return ZwCreateKey(KeyHandle
,
836 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle
,
837 OUT PUNICODE_STRING SubKeyName
,
841 PKEY_BASIC_INFORMATION KeyInfo
= NULL
;
842 ULONG BufferLength
= 0;
843 ULONG ReturnedLength
;
846 /* Check if we have a name */
847 if (SubKeyName
->MaximumLength
)
849 /* Allocate a buffer for it */
850 BufferLength
= SubKeyName
->MaximumLength
+
851 sizeof(KEY_BASIC_INFORMATION
);
852 KeyInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
853 if (!KeyInfo
) return STATUS_NO_MEMORY
;
856 /* Enumerate the key */
857 Status
= ZwEnumerateKey(KeyHandle
,
863 if (NT_SUCCESS(Status
) && (KeyInfo
!= NULL
))
865 /* Check if the name fits */
866 if (KeyInfo
->NameLength
<= SubKeyName
->MaximumLength
)
869 SubKeyName
->Length
= (USHORT
)KeyInfo
->NameLength
;
872 RtlMoveMemory(SubKeyName
->Buffer
,
878 /* Otherwise, we ran out of buffer space */
879 Status
= STATUS_BUFFER_OVERFLOW
;
883 /* Free the buffer and return status */
884 if (KeyInfo
) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo
);
893 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle
)
895 /* This just deletes the key */
896 return ZwDeleteKey(KeyHandle
);
904 RtlpNtOpenKey(OUT HANDLE KeyHandle
,
905 IN ACCESS_MASK DesiredAccess
,
906 IN POBJECT_ATTRIBUTES ObjectAttributes
,
909 /* Check if we have object attributes */
910 if (ObjectAttributes
)
912 /* Mask out the unsupported flags */
913 ObjectAttributes
->Attributes
&= ~(OBJ_PERMANENT
| OBJ_EXCLUSIVE
);
917 return ZwOpenKey(KeyHandle
, DesiredAccess
, ObjectAttributes
);
925 RtlpNtQueryValueKey(IN HANDLE KeyHandle
,
926 OUT PULONG Type OPTIONAL
,
927 OUT PVOID Data OPTIONAL
,
928 IN OUT PULONG DataLength OPTIONAL
,
931 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
932 UNICODE_STRING ValueName
;
933 ULONG BufferLength
= 0;
936 /* Clear the value name */
937 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
939 /* Check if we were already given a length */
940 if (DataLength
) BufferLength
= *DataLength
;
942 /* Add the size of the structure */
943 BufferLength
+= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
945 /* Allocate memory for the value */
946 ValueInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
947 if (!ValueInfo
) return STATUS_NO_MEMORY
;
949 /* Query the value */
950 Status
= ZwQueryValueKey(KeyHandle
,
952 KeyValuePartialInformation
,
956 if ((NT_SUCCESS(Status
)) || (Status
== STATUS_BUFFER_OVERFLOW
))
958 /* Return the length and type */
959 if (DataLength
) *DataLength
= ValueInfo
->DataLength
;
960 if (Type
) *Type
= ValueInfo
->Type
;
963 /* Check if the caller wanted data back, and we got it */
964 if ((NT_SUCCESS(Status
)) && (Data
))
967 RtlMoveMemory(Data
, ValueInfo
->Data
, ValueInfo
->DataLength
);
970 /* Free the memory and return status */
971 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo
);
980 RtlpNtSetValueKey(IN HANDLE KeyHandle
,
985 UNICODE_STRING ValueName
;
988 RtlInitEmptyUnicodeString(&ValueName
, NULL
, 0);
989 return ZwSetValueKey(KeyHandle
,
1002 RtlQueryRegistryValues(IN ULONG RelativeTo
,
1004 IN PRTL_QUERY_REGISTRY_TABLE QueryTable
,
1006 IN PVOID Environment OPTIONAL
)
1009 PKEY_VALUE_FULL_INFORMATION KeyValueInfo
= NULL
;
1010 HANDLE KeyHandle
, CurrentKey
;
1011 SIZE_T BufferSize
, InfoSize
;
1012 UNICODE_STRING KeyPath
, KeyValueName
;
1013 OBJECT_ATTRIBUTES ObjectAttributes
;
1017 /* Get the registry handle */
1018 Status
= RtlpGetRegistryHandle(RelativeTo
, Path
, FALSE
, &KeyHandle
);
1019 if (!NT_SUCCESS(Status
)) return Status
;
1021 /* Initialize the path */
1022 RtlInitUnicodeString(&KeyPath
,
1023 (RelativeTo
& RTL_REGISTRY_HANDLE
) ? NULL
: Path
);
1025 /* Allocate a query buffer */
1026 BufferSize
= RtlpAllocDeallocQueryBufferSize
;
1027 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
, NULL
, 0, &Status
);
1030 /* Close the handle if we have one and fail */
1031 RtlpCloseRegistryHandle(RelativeTo
, KeyHandle
);
1036 KeyValueInfo
->DataOffset
= 0;
1037 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1038 CurrentKey
= KeyHandle
;
1040 /* Loop the query table */
1041 while ((QueryTable
->QueryRoutine
) ||
1042 (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_SUBKEY
|
1043 RTL_QUERY_REGISTRY_DIRECT
)))
1045 /* Check if the request is invalid */
1046 if ((QueryTable
->Flags
& RTL_QUERY_REGISTRY_DIRECT
) &&
1047 (!(QueryTable
->Name
) ||
1048 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
) ||
1049 (QueryTable
->QueryRoutine
)))
1052 Status
= STATUS_INVALID_PARAMETER
;
1056 /* Check if we want a specific key */
1057 if (QueryTable
->Flags
& (RTL_QUERY_REGISTRY_TOPKEY
|
1058 RTL_QUERY_REGISTRY_SUBKEY
))
1060 /* Check if we're working with another handle */
1061 if (CurrentKey
!= KeyHandle
)
1063 /* Close our current key and use the top */
1064 NtClose(CurrentKey
);
1065 CurrentKey
= KeyHandle
;
1069 /* Check if we're querying the subkey */
1070 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_SUBKEY
)
1072 /* Make sure we have a name */
1073 if (!QueryTable
->Name
)
1076 Status
= STATUS_INVALID_PARAMETER
;
1080 /* Initialize the name */
1081 RtlInitUnicodeString(&KeyPath
, QueryTable
->Name
);
1083 /* Get the key handle */
1084 InitializeObjectAttributes(&ObjectAttributes
,
1086 OBJ_CASE_INSENSITIVE
|
1090 Status
= ZwOpenKey(&CurrentKey
,
1093 if (NT_SUCCESS(Status
))
1095 /* If we have a query routine, go enumerate values */
1096 if (QueryTable
->QueryRoutine
) goto ProcessValues
;
1100 else if (QueryTable
->Name
)
1102 /* Initialize the path */
1103 RtlInitUnicodeString(&KeyValueName
, QueryTable
->Name
);
1105 /* Start query loop */
1109 /* Make sure we didn't retry too many times */
1113 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1114 "at line %d\n", __LINE__
);
1118 /* Query key information */
1119 Status
= ZwQueryValueKey(CurrentKey
,
1121 KeyValueFullInformation
,
1125 if (Status
== STATUS_BUFFER_OVERFLOW
)
1127 /* Normalize status code */
1128 Status
= STATUS_BUFFER_TOO_SMALL
;
1131 /* Check for failure */
1132 if (!NT_SUCCESS(Status
))
1134 /* Check if we didn't find it */
1135 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1137 /* Setup a default */
1138 KeyValueInfo
->Type
= REG_NONE
;
1139 KeyValueInfo
->DataLength
= 0;
1140 ResultLength
= (ULONG
)InfoSize
;
1142 /* Call the query routine */
1143 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1150 /* Check for buffer being too small */
1151 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1153 /* Increase allocation size */
1154 BufferSize
= ResultLength
+
1156 sizeof(UNICODE_NULL
);
1157 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1161 if (!KeyValueInfo
) break;
1163 /* Update the data */
1164 KeyValueInfo
->DataOffset
= 0;
1165 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1171 /* Check if this is a multi-string */
1172 if (KeyValueInfo
->Type
== REG_MULTI_SZ
)
1174 /* Add a null-char */
1175 ((PWCHAR
)KeyValueInfo
)[ResultLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1176 KeyValueInfo
->DataLength
+= sizeof(UNICODE_NULL
);
1179 /* Call the query routine */
1180 ResultLength
= (ULONG
)InfoSize
;
1181 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1187 /* Check for buffer being too small */
1188 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1190 /* Increase allocation size */
1191 BufferSize
= ResultLength
+
1193 sizeof(UNICODE_NULL
);
1194 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1198 if (!KeyValueInfo
) break;
1200 /* Update the data */
1201 KeyValueInfo
->DataOffset
= 0;
1202 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1206 /* Check if we need to delete the key */
1207 if ((NT_SUCCESS(Status
)) &&
1208 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
))
1211 ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1215 /* We're done, break out */
1219 else if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_NOVALUE
)
1221 /* Just call the query routine */
1222 Status
= QueryTable
->QueryRoutine(NULL
,
1227 QueryTable
->EntryContext
);
1232 /* Loop every value */
1236 /* Enumerate the keys */
1237 Status
= ZwEnumerateValueKey(CurrentKey
,
1239 KeyValueFullInformation
,
1243 if (Status
== STATUS_BUFFER_OVERFLOW
)
1245 /* Normalize the status */
1246 Status
= STATUS_BUFFER_TOO_SMALL
;
1249 /* Check if we found all the entries */
1250 if (Status
== STATUS_NO_MORE_ENTRIES
)
1252 /* Check if this was the first entry and caller needs it */
1254 (QueryTable
->Flags
& RTL_QUERY_REGISTRY_REQUIRED
))
1257 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1261 /* Otherwise, it's ok */
1262 Status
= STATUS_SUCCESS
;
1267 /* Check if enumeration worked */
1268 if (NT_SUCCESS(Status
))
1270 /* Call the query routine */
1271 ResultLength
= (ULONG
)InfoSize
;
1272 Status
= RtlpCallQueryRegistryRoutine(QueryTable
,
1279 /* Check if the query failed */
1280 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1282 /* Increase allocation size */
1283 BufferSize
= ResultLength
+
1285 sizeof(UNICODE_NULL
);
1286 KeyValueInfo
= RtlpAllocDeallocQueryBuffer(&BufferSize
,
1290 if (!KeyValueInfo
) break;
1292 /* Update the data */
1293 KeyValueInfo
->DataOffset
= 0;
1294 InfoSize
= BufferSize
- sizeof(UNICODE_NULL
);
1296 /* Try the value again unless it's been too many times */
1297 if (i
++ <= 4) continue;
1301 /* Break out if we failed */
1302 if (!NT_SUCCESS(Status
)) break;
1304 /* Reset the number of retries and check if we need to delete */
1306 if (QueryTable
->Flags
& RTL_QUERY_REGISTRY_DELETE
)
1308 /* Build the name */
1309 RtlInitEmptyUnicodeString(&KeyValueName
,
1311 (USHORT
)KeyValueInfo
->NameLength
);
1312 KeyValueName
.Length
= KeyValueName
.MaximumLength
;
1314 /* Delete the key */
1315 Status
= ZwDeleteValueKey(CurrentKey
, &KeyValueName
);
1316 if (NT_SUCCESS(Status
)) Value
--;
1319 /* Go to the next value */
1324 /* Check if we failed anywhere along the road */
1325 if (!NT_SUCCESS(Status
)) break;
1331 /* Check if we need to close our handle */
1332 if (KeyHandle
) RtlpCloseRegistryHandle(RelativeTo
, KeyHandle
);
1333 if ((CurrentKey
) && (CurrentKey
!= KeyHandle
)) ZwClose(CurrentKey
);
1335 /* Free our buffer and return status */
1336 RtlpAllocDeallocQueryBuffer(NULL
, KeyValueInfo
, BufferSize
, NULL
);