[RTL]
[reactos.git] / reactos / lib / rtl / registry.c
1 /*
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)
7 * Eric Kohl
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <rtl.h>
13
14 #include <ndk/cmfuncs.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #define TAG_RTLREGISTRY 'vrqR'
20
21 extern SIZE_T RtlpAllocDeallocQueryBufferSize;
22
23 /* DATA **********************************************************************/
24
25 PCWSTR RtlpRegPaths[RTL_REGISTRY_MAXIMUM] =
26 {
27 NULL,
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",
33 };
34
35 /* PRIVATE FUNCTIONS *********************************************************/
36
37 NTSTATUS
38 NTAPI
39 RtlpQueryRegistryDirect(IN ULONG ValueType,
40 IN PVOID ValueData,
41 IN ULONG ValueLength,
42 IN PVOID Buffer)
43 {
44 USHORT ActualLength;
45 PUNICODE_STRING ReturnString = Buffer;
46 PULONG Length = Buffer;
47 ULONG RealLength;
48
49 /* Check if this is a string */
50 if ((ValueType == REG_SZ) ||
51 (ValueType == REG_EXPAND_SZ) ||
52 (ValueType == REG_MULTI_SZ))
53 {
54 /* Normalize the length */
55 if (ValueLength > MAXUSHORT)
56 ActualLength = MAXUSHORT;
57 else
58 ActualLength = (USHORT)ValueLength;
59
60 /* Check if the return string has been allocated */
61 if (!ReturnString->Buffer)
62 {
63 /* Allocate it */
64 ReturnString->Buffer = RtlpAllocateMemory(ActualLength, TAG_RTLREGISTRY);
65 if (!ReturnString->Buffer) return STATUS_NO_MEMORY;
66 ReturnString->MaximumLength = ActualLength;
67 }
68 else if (ActualLength > ReturnString->MaximumLength)
69 {
70 /* The string the caller allocated is too small */
71 return STATUS_BUFFER_TOO_SMALL;
72 }
73
74 /* Copy the data */
75 RtlCopyMemory(ReturnString->Buffer, ValueData, ActualLength);
76 ReturnString->Length = ActualLength - sizeof(UNICODE_NULL);
77 }
78 else if (ValueLength <= sizeof(ULONG))
79 {
80 /* Check if we can just copy the data */
81 if ((Buffer != ValueData) && (ValueLength))
82 {
83 /* Copy it */
84 RtlCopyMemory(Buffer, ValueData, ValueLength);
85 }
86 }
87 else
88 {
89 /* Check if the length is negative */
90 if ((LONG)*Length < 0)
91 {
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);
96 }
97 else
98 {
99 /* Check if there's space for the length and type, plus data */
100 if (*Length < (2 * sizeof(ULONG) + ValueLength))
101 {
102 /* Nope, fail */
103 return STATUS_BUFFER_TOO_SMALL;
104 }
105
106 /* Return the data */
107 *Length++ = ValueLength;
108 *Length++ = ValueType;
109 RtlCopyMemory(Length, ValueData, ValueLength);
110 }
111 }
112
113 /* All done */
114 return STATUS_SUCCESS;
115 }
116
117 NTSTATUS
118 NTAPI
119 RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
120 IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo,
121 IN OUT PULONG InfoSize,
122 IN PVOID Context,
123 IN PVOID Environment)
124 {
125 ULONG InfoLength;
126 SIZE_T Length, SpareLength, c;
127 ULONG RequiredLength;
128 PCHAR SpareData, DataEnd;
129 ULONG Type;
130 PWCHAR Name, p, ValueEnd;
131 PVOID Data;
132 NTSTATUS Status;
133 BOOLEAN FoundExpander = FALSE;
134 UNICODE_STRING Source, Destination;
135
136 /* Setup defaults */
137 InfoLength = *InfoSize;
138 *InfoSize = 0;
139
140 /* Check if there's no data */
141 if (KeyValueInfo->DataOffset == MAXULONG)
142 {
143 /* Return proper status code */
144 return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ?
145 STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS;
146 }
147
148 /* Setup spare data pointers */
149 SpareData = (PCHAR)KeyValueInfo;
150 SpareLength = InfoLength;
151 DataEnd = SpareData + SpareLength;
152
153 /* Check if there's no value or data */
154 if ((KeyValueInfo->Type == REG_NONE) ||
155 (!(KeyValueInfo->DataLength) &&
156 (KeyValueInfo->Type == QueryTable->DefaultType)))
157 {
158 /* Check if there's no value */
159 if (QueryTable->DefaultType == REG_NONE)
160 {
161 /* Return proper status code */
162 return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ?
163 STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS;
164 }
165
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;
171 if (!Length)
172 {
173 /* No default length given, try to calculate it */
174 p = Data;
175 if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
176 {
177 /* This is a string, count the characters */
178 while (*p++);
179 Length = (ULONG_PTR)p - (ULONG_PTR)Data;
180 }
181 else if (Type == REG_MULTI_SZ)
182 {
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);
186 }
187 }
188 }
189 else
190 {
191 /* Check if this isn't a direct return */
192 if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT))
193 {
194 /* Check if we have length */
195 if (KeyValueInfo->DataLength)
196 {
197 /* Increase the spare data */
198 SpareData += KeyValueInfo->DataOffset +
199 KeyValueInfo->DataLength;
200 }
201 else
202 {
203 /* Otherwise, the spare data only has the name data */
204 SpareData += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
205 KeyValueInfo->NameLength;
206 }
207
208 /* Align the pointer and get new size of spare data */
209 SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
210 SpareLength = DataEnd - SpareData;
211
212 /* Check if we have space to copy the data */
213 RequiredLength = KeyValueInfo->NameLength + sizeof(UNICODE_NULL);
214 if ((SpareData > DataEnd) || (SpareLength < RequiredLength))
215 {
216 /* Fail and return the missing length */
217 *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + RequiredLength;
218 return STATUS_BUFFER_TOO_SMALL;
219 }
220
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;
225
226 /* Update the spare data information */
227 SpareData += RequiredLength;
228 SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
229 SpareLength = DataEnd - SpareData;
230 }
231 else
232 {
233 /* Just return the name */
234 Name = (PWCHAR)QueryTable->Name;
235 }
236
237 /* Capture key data */
238 Type = KeyValueInfo->Type;
239 Data = (PVOID)((ULONG_PTR)KeyValueInfo + KeyValueInfo->DataOffset);
240 Length = KeyValueInfo->DataLength;
241 }
242
243 /* Check if we're expanding */
244 if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
245 {
246 /* Check if it's a multi-string */
247 if (Type == REG_MULTI_SZ)
248 {
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));
253 p = Data;
254
255 /* Loop all strings */
256 while (p < ValueEnd)
257 {
258 /* Go to the next string */
259 while (*p++);
260
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)
264 {
265 /* Do the query */
266 Status = RtlpQueryRegistryDirect(REG_SZ,
267 Data,
268 (ULONG)Length,
269 QueryTable->EntryContext);
270 QueryTable->EntryContext =
271 (PVOID)((ULONG_PTR)QueryTable->EntryContext +
272 sizeof(UNICODE_STRING));
273 }
274 else
275 {
276 /* Call the custom routine */
277 Status = QueryTable->QueryRoutine(Name,
278 REG_SZ,
279 Data,
280 (ULONG)Length,
281 Context,
282 QueryTable->EntryContext);
283 }
284
285 /* Normalize status */
286 if (Status == STATUS_BUFFER_TOO_SMALL) Status = STATUS_SUCCESS;
287 if (!NT_SUCCESS(Status)) break;
288
289 /* Update data pointer */
290 Data = p;
291 }
292
293 /* Return */
294 return Status;
295 }
296
297 /* Check if this is an expand string */
298 if ((Type == REG_EXPAND_SZ) && (Length >= sizeof(WCHAR)))
299 {
300 /* Try to find the expander */
301 c = Length - sizeof(UNICODE_NULL);
302 p = (PWCHAR)Data;
303 while (c)
304 {
305 /* Check if this is one */
306 if (*p == L'%')
307 {
308 /* Yup! */
309 FoundExpander = TRUE;
310 break;
311 }
312
313 /* Continue in the buffer */
314 p++;
315 c -= sizeof(WCHAR);
316 }
317
318 /* So check if we have one */
319 if (FoundExpander)
320 {
321 /* Setup the source string */
322 RtlInitEmptyUnicodeString(&Source, Data, (USHORT)Length);
323 Source.Length = Source.MaximumLength - sizeof(UNICODE_NULL);
324
325 /* Setup the desination string */
326 RtlInitEmptyUnicodeString(&Destination, (PWCHAR)SpareData, 0);
327
328 /* Check if we're out of space */
329 if (SpareLength <= 0)
330 {
331 /* Then we don't have any space in our string */
332 Destination.MaximumLength = 0;
333 }
334 else if (SpareLength <= MAXUSHORT)
335 {
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;
339 }
340 else
341 {
342 /* We can't fit into a string, so truncate */
343 Destination.MaximumLength = MAXUSHORT;
344 Destination.Buffer[MAXUSHORT / sizeof(WCHAR) - 1] = UNICODE_NULL;
345 }
346
347 /* Expand the strings and set our type as one string */
348 Status = RtlExpandEnvironmentStrings_U(Environment,
349 &Source,
350 &Destination,
351 &RequiredLength);
352 Type = REG_SZ;
353
354 /* Check for success */
355 if (NT_SUCCESS(Status))
356 {
357 /* Set the value name and length to our string */
358 Data = Destination.Buffer;
359 Length = Destination.Length + sizeof(UNICODE_NULL);
360 }
361 else
362 {
363 /* Check if our buffer is too small */
364 if (Status == STATUS_BUFFER_TOO_SMALL)
365 {
366 /* Set the required missing length */
367 *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) +
368 RequiredLength;
369
370 /* Notify debugger */
371 DPRINT1("RTL: Expand variables for %wZ failed - "
372 "Status == %lx Size %x > %x <%x>\n",
373 &Source,
374 Status,
375 *InfoSize,
376 InfoLength,
377 Destination.MaximumLength);
378 }
379 else
380 {
381 /* Notify debugger */
382 DPRINT1("RTL: Expand variables for %wZ failed - "
383 "Status == %lx\n",
384 &Source,
385 Status);
386 }
387
388 /* Return the status */
389 return Status;
390 }
391 }
392 }
393 }
394
395 /* Check if this is a direct query */
396 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)
397 {
398 /* Return the data */
399 Status = RtlpQueryRegistryDirect(Type,
400 Data,
401 (ULONG)Length,
402 QueryTable->EntryContext);
403 }
404 else
405 {
406 /* Call the query routine */
407 Status = QueryTable->QueryRoutine(Name,
408 Type,
409 Data,
410 (ULONG)Length,
411 Context,
412 QueryTable->EntryContext);
413 }
414
415 /* Normalize and return status */
416 return (Status == STATUS_BUFFER_TOO_SMALL) ? STATUS_SUCCESS : Status;
417 }
418
419 _Success_(return!=NULL || BufferSize==0)
420 _When_(BufferSize!=NULL,__drv_allocatesMem(Mem))
421 PVOID
422 NTAPI
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)
428 {
429 PVOID Buffer = NULL;
430
431 /* Assume success */
432 if (Status) *Status = STATUS_SUCCESS;
433
434 /* Free the old buffer */
435 if (OldBuffer) RtlpFreeMemory(OldBuffer, TAG_RTLREGISTRY);
436
437 /* Check if we need to allocate a new one */
438 if (BufferSize)
439 {
440 /* Allocate */
441 Buffer = RtlpAllocateMemory(*BufferSize, TAG_RTLREGISTRY);
442 if (!(Buffer) && (Status)) *Status = STATUS_NO_MEMORY;
443 }
444
445 /* Return the pointer */
446 return Buffer;
447 }
448
449 NTSTATUS
450 NTAPI
451 RtlpGetRegistryHandle(IN ULONG RelativeTo,
452 IN PCWSTR Path,
453 IN BOOLEAN Create,
454 IN PHANDLE KeyHandle)
455 {
456 UNICODE_STRING KeyPath, KeyName;
457 WCHAR KeyBuffer[MAX_PATH];
458 OBJECT_ATTRIBUTES ObjectAttributes;
459 NTSTATUS Status;
460
461 /* Check if we just want the handle */
462 if (RelativeTo & RTL_REGISTRY_HANDLE)
463 {
464 *KeyHandle = (HANDLE)Path;
465 return STATUS_SUCCESS;
466 }
467
468 /* Check for optional flag */
469 if (RelativeTo & RTL_REGISTRY_OPTIONAL)
470 {
471 /* Mask it out */
472 RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
473 }
474
475 /* Fail on invalid parameter */
476 if (RelativeTo >= RTL_REGISTRY_MAXIMUM) return STATUS_INVALID_PARAMETER;
477
478 /* Initialize the key name */
479 RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer));
480
481 /* Check if we have to lookup a path to prefix */
482 if (RelativeTo != RTL_REGISTRY_ABSOLUTE)
483 {
484 /* Check if we need the current user key */
485 if (RelativeTo == RTL_REGISTRY_USER)
486 {
487 /* Get the user key path */
488 Status = RtlFormatCurrentUserKeyPath(&KeyPath);
489
490 /* Check if it worked */
491 if (NT_SUCCESS(Status))
492 {
493 /* Append the user key path */
494 Status = RtlAppendUnicodeStringToString(&KeyName, &KeyPath);
495
496 /* Free the user key path */
497 RtlFreeUnicodeString (&KeyPath);
498 }
499 else
500 {
501 /* It didn't work so fall back to the default user key */
502 Status = RtlAppendUnicodeToString(&KeyName, RtlpRegPaths[RTL_REGISTRY_USER]);
503 }
504 }
505 else
506 {
507 /* Get one of the prefixes */
508 Status = RtlAppendUnicodeToString(&KeyName,
509 RtlpRegPaths[RelativeTo]);
510 }
511
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;
516 }
517
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;
522
523 /* Initialize the object attributes */
524 InitializeObjectAttributes(&ObjectAttributes,
525 &KeyName,
526 OBJ_CASE_INSENSITIVE,
527 NULL,
528 NULL);
529
530 /* Check if we want to create it */
531 if (Create)
532 {
533 /* Create the key with write privileges */
534 Status = ZwCreateKey(KeyHandle,
535 GENERIC_WRITE,
536 &ObjectAttributes,
537 0,
538 NULL,
539 0,
540 NULL);
541 }
542 else
543 {
544 /* Otherwise, just open it with read access */
545 Status = ZwOpenKey(KeyHandle,
546 MAXIMUM_ALLOWED | GENERIC_READ,
547 &ObjectAttributes);
548 }
549
550 /* Return status */
551 return Status;
552 }
553
554 FORCEINLINE
555 VOID
556 RtlpCloseRegistryHandle(
557 _In_ ULONG RelativeTo,
558 _In_ HANDLE KeyHandle)
559 {
560 /* Did the caller pass a key handle? */
561 if (!(RelativeTo & RTL_REGISTRY_HANDLE))
562 {
563 /* We opened the key in RtlpGetRegistryHandle, so close it now */
564 ZwClose(KeyHandle);
565 }
566 }
567
568 /* PUBLIC FUNCTIONS **********************************************************/
569
570 /*
571 * @implemented
572 */
573 NTSTATUS
574 NTAPI
575 RtlCheckRegistryKey(IN ULONG RelativeTo,
576 IN PWSTR Path)
577 {
578 HANDLE KeyHandle;
579 NTSTATUS Status;
580 PAGED_CODE_RTL();
581
582 /* Call the helper */
583 Status = RtlpGetRegistryHandle(RelativeTo,
584 Path,
585 FALSE,
586 &KeyHandle);
587 if (!NT_SUCCESS(Status)) return Status;
588
589 /* Close the handle even for RTL_REGISTRY_HANDLE */
590 ZwClose(KeyHandle);
591 return STATUS_SUCCESS;
592 }
593
594 /*
595 * @implemented
596 */
597 NTSTATUS
598 NTAPI
599 RtlCreateRegistryKey(IN ULONG RelativeTo,
600 IN PWSTR Path)
601 {
602 HANDLE KeyHandle;
603 NTSTATUS Status;
604 PAGED_CODE_RTL();
605
606 /* Call the helper */
607 Status = RtlpGetRegistryHandle(RelativeTo,
608 Path,
609 TRUE,
610 &KeyHandle);
611 if (!NT_SUCCESS(Status)) return Status;
612
613 /* All went well, close the handle and return status */
614 RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
615 return STATUS_SUCCESS;
616 }
617
618 /*
619 * @implemented
620 */
621 NTSTATUS
622 NTAPI
623 RtlDeleteRegistryValue(IN ULONG RelativeTo,
624 IN PCWSTR Path,
625 IN PCWSTR ValueName)
626 {
627 HANDLE KeyHandle;
628 NTSTATUS Status;
629 UNICODE_STRING Name;
630 PAGED_CODE_RTL();
631
632 /* Call the helper */
633 Status = RtlpGetRegistryHandle(RelativeTo,
634 Path,
635 TRUE,
636 &KeyHandle);
637 if (!NT_SUCCESS(Status)) return Status;
638
639 /* Initialize the key name and delete it */
640 RtlInitUnicodeString(&Name, ValueName);
641 Status = ZwDeleteValueKey(KeyHandle, &Name);
642
643 /* Close the handle and return status */
644 RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
645 return Status;
646 }
647
648 /*
649 * @implemented
650 */
651 NTSTATUS
652 NTAPI
653 RtlWriteRegistryValue(IN ULONG RelativeTo,
654 IN PCWSTR Path,
655 IN PCWSTR ValueName,
656 IN ULONG ValueType,
657 IN PVOID ValueData,
658 IN ULONG ValueLength)
659 {
660 HANDLE KeyHandle;
661 NTSTATUS Status;
662 UNICODE_STRING Name;
663 PAGED_CODE_RTL();
664
665 /* Call the helper */
666 Status = RtlpGetRegistryHandle(RelativeTo,
667 Path,
668 TRUE,
669 &KeyHandle);
670 if (!NT_SUCCESS(Status)) return Status;
671
672 /* Initialize the key name and set it */
673 RtlInitUnicodeString(&Name, ValueName);
674 Status = ZwSetValueKey(KeyHandle,
675 &Name,
676 0,
677 ValueType,
678 ValueData,
679 ValueLength);
680
681 /* Close the handle and return status */
682 RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
683 return Status;
684 }
685
686 /*
687 * @implemented
688 */
689 NTSTATUS
690 NTAPI
691 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,
692 OUT PHANDLE KeyHandle)
693 {
694 OBJECT_ATTRIBUTES ObjectAttributes;
695 UNICODE_STRING KeyPath;
696 NTSTATUS Status;
697 PAGED_CODE_RTL();
698
699 /* Get the user key */
700 Status = RtlFormatCurrentUserKeyPath(&KeyPath);
701 if (NT_SUCCESS(Status))
702 {
703 /* Initialize the attributes and open it */
704 InitializeObjectAttributes(&ObjectAttributes,
705 &KeyPath,
706 OBJ_CASE_INSENSITIVE,
707 NULL,
708 NULL);
709 Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
710
711 /* Free the path and return success if it worked */
712 RtlFreeUnicodeString(&KeyPath);
713 if (NT_SUCCESS(Status)) return STATUS_SUCCESS;
714 }
715
716 /* It didn't work, so use the default key */
717 RtlInitUnicodeString(&KeyPath, RtlpRegPaths[RTL_REGISTRY_USER]);
718 InitializeObjectAttributes(&ObjectAttributes,
719 &KeyPath,
720 OBJ_CASE_INSENSITIVE,
721 NULL,
722 NULL);
723 Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
724
725 /* Return status */
726 return Status;
727 }
728
729 /*
730 * @implemented
731 */
732 NTSTATUS
733 NTAPI
734 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath)
735 {
736 HANDLE TokenHandle;
737 UCHAR Buffer[256];
738 PSID_AND_ATTRIBUTES SidBuffer;
739 ULONG Length;
740 UNICODE_STRING SidString;
741 NTSTATUS Status;
742 PAGED_CODE_RTL();
743
744 /* Open the thread token */
745 Status = ZwOpenThreadToken(NtCurrentThread(),
746 TOKEN_QUERY,
747 TRUE,
748 &TokenHandle);
749 if (!NT_SUCCESS(Status))
750 {
751 /* We failed, is it because we don't have a thread token? */
752 if (Status != STATUS_NO_TOKEN) return Status;
753
754 /* It is, so use the process token */
755 Status = ZwOpenProcessToken(NtCurrentProcess(),
756 TOKEN_QUERY,
757 &TokenHandle);
758 if (!NT_SUCCESS(Status)) return Status;
759 }
760
761 /* Now query the token information */
762 SidBuffer = (PSID_AND_ATTRIBUTES)Buffer;
763 Status = ZwQueryInformationToken(TokenHandle,
764 TokenUser,
765 (PVOID)SidBuffer,
766 sizeof(Buffer),
767 &Length);
768
769 /* Close the handle and handle failure */
770 ZwClose(TokenHandle);
771 if (!NT_SUCCESS(Status)) return Status;
772
773 /* Convert the SID */
774 Status = RtlConvertSidToUnicodeString(&SidString, SidBuffer[0].Sid, TRUE);
775 if (!NT_SUCCESS(Status)) return Status;
776
777 /* Add the length of the prefix */
778 Length = SidString.Length + sizeof(L"\\REGISTRY\\USER\\");
779
780 /* Initialize a string */
781 RtlInitEmptyUnicodeString(KeyPath,
782 RtlpAllocateStringMemory(Length, TAG_USTR),
783 (USHORT)Length);
784 if (!KeyPath->Buffer)
785 {
786 /* Free the string and fail */
787 RtlFreeUnicodeString(&SidString);
788 return STATUS_NO_MEMORY;
789 }
790
791 /* Append the prefix and SID */
792 RtlAppendUnicodeToString(KeyPath, L"\\REGISTRY\\USER\\");
793 RtlAppendUnicodeStringToString(KeyPath, &SidString);
794
795 /* Free the temporary string and return success */
796 RtlFreeUnicodeString(&SidString);
797 return STATUS_SUCCESS;
798 }
799
800 /*
801 * @implemented
802 */
803 NTSTATUS
804 NTAPI
805 RtlpNtCreateKey(OUT HANDLE KeyHandle,
806 IN ACCESS_MASK DesiredAccess,
807 IN POBJECT_ATTRIBUTES ObjectAttributes,
808 IN ULONG TitleIndex,
809 IN PUNICODE_STRING Class,
810 OUT PULONG Disposition)
811 {
812 /* Check if we have object attributes */
813 if (ObjectAttributes)
814 {
815 /* Mask out the unsupported flags */
816 ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE);
817 }
818
819 /* Create the key */
820 return ZwCreateKey(KeyHandle,
821 DesiredAccess,
822 ObjectAttributes,
823 0,
824 NULL,
825 0,
826 Disposition);
827 }
828
829 /*
830 * @implemented
831 */
832 NTSTATUS
833 NTAPI
834 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle,
835 OUT PUNICODE_STRING SubKeyName,
836 IN ULONG Index,
837 IN ULONG Unused)
838 {
839 PKEY_BASIC_INFORMATION KeyInfo = NULL;
840 ULONG BufferLength = 0;
841 ULONG ReturnedLength;
842 NTSTATUS Status;
843
844 /* Check if we have a name */
845 if (SubKeyName->MaximumLength)
846 {
847 /* Allocate a buffer for it */
848 BufferLength = SubKeyName->MaximumLength +
849 sizeof(KEY_BASIC_INFORMATION);
850 KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
851 if (!KeyInfo) return STATUS_NO_MEMORY;
852 }
853
854 /* Enumerate the key */
855 Status = ZwEnumerateKey(KeyHandle,
856 Index,
857 KeyBasicInformation,
858 KeyInfo,
859 BufferLength,
860 &ReturnedLength);
861 if (NT_SUCCESS(Status) && (KeyInfo != NULL))
862 {
863 /* Check if the name fits */
864 if (KeyInfo->NameLength <= SubKeyName->MaximumLength)
865 {
866 /* Set the length */
867 SubKeyName->Length = (USHORT)KeyInfo->NameLength;
868
869 /* Copy it */
870 RtlMoveMemory(SubKeyName->Buffer,
871 KeyInfo->Name,
872 SubKeyName->Length);
873 }
874 else
875 {
876 /* Otherwise, we ran out of buffer space */
877 Status = STATUS_BUFFER_OVERFLOW;
878 }
879 }
880
881 /* Free the buffer and return status */
882 if (KeyInfo) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
883 return Status;
884 }
885
886 /*
887 * @implemented
888 */
889 NTSTATUS
890 NTAPI
891 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle)
892 {
893 /* This just deletes the key */
894 return ZwDeleteKey(KeyHandle);
895 }
896
897 /*
898 * @implemented
899 */
900 NTSTATUS
901 NTAPI
902 RtlpNtOpenKey(OUT HANDLE KeyHandle,
903 IN ACCESS_MASK DesiredAccess,
904 IN POBJECT_ATTRIBUTES ObjectAttributes,
905 IN ULONG Unused)
906 {
907 /* Check if we have object attributes */
908 if (ObjectAttributes)
909 {
910 /* Mask out the unsupported flags */
911 ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE);
912 }
913
914 /* Open the key */
915 return ZwOpenKey(KeyHandle, DesiredAccess, ObjectAttributes);
916 }
917
918 /*
919 * @implemented
920 */
921 NTSTATUS
922 NTAPI
923 RtlpNtQueryValueKey(IN HANDLE KeyHandle,
924 OUT PULONG Type OPTIONAL,
925 OUT PVOID Data OPTIONAL,
926 IN OUT PULONG DataLength OPTIONAL,
927 IN ULONG Unused)
928 {
929 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
930 UNICODE_STRING ValueName;
931 ULONG BufferLength = 0;
932 NTSTATUS Status;
933
934 /* Clear the value name */
935 RtlInitEmptyUnicodeString(&ValueName, NULL, 0);
936
937 /* Check if we were already given a length */
938 if (DataLength) BufferLength = *DataLength;
939
940 /* Add the size of the structure */
941 BufferLength += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
942
943 /* Allocate memory for the value */
944 ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
945 if (!ValueInfo) return STATUS_NO_MEMORY;
946
947 /* Query the value */
948 Status = ZwQueryValueKey(KeyHandle,
949 &ValueName,
950 KeyValuePartialInformation,
951 ValueInfo,
952 BufferLength,
953 &BufferLength);
954 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW))
955 {
956 /* Return the length and type */
957 if (DataLength) *DataLength = ValueInfo->DataLength;
958 if (Type) *Type = ValueInfo->Type;
959 }
960
961 /* Check if the caller wanted data back, and we got it */
962 if ((NT_SUCCESS(Status)) && (Data))
963 {
964 /* Copy it */
965 RtlMoveMemory(Data, ValueInfo->Data, ValueInfo->DataLength);
966 }
967
968 /* Free the memory and return status */
969 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo);
970 return Status;
971 }
972
973 /*
974 * @implemented
975 */
976 NTSTATUS
977 NTAPI
978 RtlpNtSetValueKey(IN HANDLE KeyHandle,
979 IN ULONG Type,
980 IN PVOID Data,
981 IN ULONG DataLength)
982 {
983 UNICODE_STRING ValueName;
984
985 /* Set the value */
986 RtlInitEmptyUnicodeString(&ValueName, NULL, 0);
987 return ZwSetValueKey(KeyHandle,
988 &ValueName,
989 0,
990 Type,
991 Data,
992 DataLength);
993 }
994
995 /*
996 * @implemented
997 */
998 NTSTATUS
999 NTAPI
1000 RtlQueryRegistryValues(IN ULONG RelativeTo,
1001 IN PCWSTR Path,
1002 IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
1003 IN PVOID Context,
1004 IN PVOID Environment OPTIONAL)
1005 {
1006 NTSTATUS Status;
1007 PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL;
1008 HANDLE KeyHandle, CurrentKey;
1009 SIZE_T BufferSize, InfoSize;
1010 UNICODE_STRING KeyPath, KeyValueName;
1011 OBJECT_ATTRIBUTES ObjectAttributes;
1012 ULONG i, Value;
1013 ULONG ResultLength;
1014
1015 /* Get the registry handle */
1016 Status = RtlpGetRegistryHandle(RelativeTo, Path, FALSE, &KeyHandle);
1017 if (!NT_SUCCESS(Status)) return Status;
1018
1019 /* Initialize the path */
1020 RtlInitUnicodeString(&KeyPath,
1021 (RelativeTo & RTL_REGISTRY_HANDLE) ? NULL : Path);
1022
1023 /* Allocate a query buffer */
1024 BufferSize = RtlpAllocDeallocQueryBufferSize;
1025 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, NULL, 0, &Status);
1026 if (!KeyValueInfo)
1027 {
1028 /* Close the handle if we have one and fail */
1029 RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
1030 return Status;
1031 }
1032
1033 /* Set defaults */
1034 KeyValueInfo->DataOffset = 0;
1035 InfoSize = BufferSize - sizeof(UNICODE_NULL);
1036 CurrentKey = KeyHandle;
1037
1038 /* Loop the query table */
1039 while ((QueryTable->QueryRoutine) ||
1040 (QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY |
1041 RTL_QUERY_REGISTRY_DIRECT)))
1042 {
1043 /* Check if the request is invalid */
1044 if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
1045 (!(QueryTable->Name) ||
1046 (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
1047 (QueryTable->QueryRoutine)))
1048 {
1049 /* Fail */
1050 Status = STATUS_INVALID_PARAMETER;
1051 break;
1052 }
1053
1054 /* Check if we want a specific key */
1055 if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY |
1056 RTL_QUERY_REGISTRY_SUBKEY))
1057 {
1058 /* Check if we're working with another handle */
1059 if (CurrentKey != KeyHandle)
1060 {
1061 /* Close our current key and use the top */
1062 NtClose(CurrentKey);
1063 CurrentKey = KeyHandle;
1064 }
1065 }
1066
1067 /* Check if we're querying the subkey */
1068 if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY)
1069 {
1070 /* Make sure we have a name */
1071 if (!QueryTable->Name)
1072 {
1073 /* Fail */
1074 Status = STATUS_INVALID_PARAMETER;
1075 }
1076 else
1077 {
1078 /* Initialize the name */
1079 RtlInitUnicodeString(&KeyPath, QueryTable->Name);
1080
1081 /* Get the key handle */
1082 InitializeObjectAttributes(&ObjectAttributes,
1083 &KeyPath,
1084 OBJ_CASE_INSENSITIVE |
1085 OBJ_KERNEL_HANDLE,
1086 KeyHandle,
1087 NULL);
1088 Status = ZwOpenKey(&CurrentKey,
1089 MAXIMUM_ALLOWED,
1090 &ObjectAttributes);
1091 if (NT_SUCCESS(Status))
1092 {
1093 /* If we have a query routine, go enumerate values */
1094 if (QueryTable->QueryRoutine) goto ProcessValues;
1095 }
1096 }
1097 }
1098 else if (QueryTable->Name)
1099 {
1100 /* Initialize the path */
1101 RtlInitUnicodeString(&KeyValueName, QueryTable->Name);
1102
1103 /* Start query loop */
1104 i = 0;
1105 while (TRUE)
1106 {
1107 /* Make sure we didn't retry too many times */
1108 if (i++ > 4)
1109 {
1110 /* Fail */
1111 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
1112 "at line %d\n", __LINE__);
1113 break;
1114 }
1115
1116 /* Query key information */
1117 Status = ZwQueryValueKey(CurrentKey,
1118 &KeyValueName,
1119 KeyValueFullInformation,
1120 KeyValueInfo,
1121 (ULONG)InfoSize,
1122 &ResultLength);
1123 if (Status == STATUS_BUFFER_OVERFLOW)
1124 {
1125 /* Normalize status code */
1126 Status = STATUS_BUFFER_TOO_SMALL;
1127 }
1128
1129 /* Check for failure */
1130 if (!NT_SUCCESS(Status))
1131 {
1132 /* Check if we didn't find it */
1133 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1134 {
1135 /* Setup a default */
1136 KeyValueInfo->Type = REG_NONE;
1137 KeyValueInfo->DataLength = 0;
1138 ResultLength = (ULONG)InfoSize;
1139
1140 /* Call the query routine */
1141 Status = RtlpCallQueryRegistryRoutine(QueryTable,
1142 KeyValueInfo,
1143 &ResultLength,
1144 Context,
1145 Environment);
1146 }
1147
1148 /* Check for buffer being too small */
1149 if (Status == STATUS_BUFFER_TOO_SMALL)
1150 {
1151 /* Increase allocation size */
1152 BufferSize = ResultLength +
1153 sizeof(ULONG_PTR) +
1154 sizeof(UNICODE_NULL);
1155 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
1156 KeyValueInfo,
1157 BufferSize,
1158 &Status);
1159 if (!KeyValueInfo) break;
1160
1161 /* Update the data */
1162 KeyValueInfo->DataOffset = 0;
1163 InfoSize = BufferSize - sizeof(UNICODE_NULL);
1164 continue;
1165 }
1166 }
1167 else
1168 {
1169 /* Check if this is a multi-string */
1170 if (KeyValueInfo->Type == REG_MULTI_SZ)
1171 {
1172 /* Add a null-char */
1173 ((PWCHAR)KeyValueInfo)[ResultLength / sizeof(WCHAR)] = UNICODE_NULL;
1174 KeyValueInfo->DataLength += sizeof(UNICODE_NULL);
1175 }
1176
1177 /* Call the query routine */
1178 ResultLength = (ULONG)InfoSize;
1179 Status = RtlpCallQueryRegistryRoutine(QueryTable,
1180 KeyValueInfo,
1181 &ResultLength,
1182 Context,
1183 Environment);
1184
1185 /* Check for buffer being too small */
1186 if (Status == STATUS_BUFFER_TOO_SMALL)
1187 {
1188 /* Increase allocation size */
1189 BufferSize = ResultLength +
1190 sizeof(ULONG_PTR) +
1191 sizeof(UNICODE_NULL);
1192 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
1193 KeyValueInfo,
1194 BufferSize,
1195 &Status);
1196 if (!KeyValueInfo) break;
1197
1198 /* Update the data */
1199 KeyValueInfo->DataOffset = 0;
1200 InfoSize = BufferSize - sizeof(UNICODE_NULL);
1201 continue;
1202 }
1203
1204 /* Check if we need to delete the key */
1205 if ((NT_SUCCESS(Status)) &&
1206 (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE))
1207 {
1208 /* Delete it */
1209 ZwDeleteValueKey(CurrentKey, &KeyValueName);
1210 }
1211 }
1212
1213 /* We're done, break out */
1214 break;
1215 }
1216 }
1217 else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE)
1218 {
1219 /* Just call the query routine */
1220 Status = QueryTable->QueryRoutine(NULL,
1221 REG_NONE,
1222 NULL,
1223 0,
1224 Context,
1225 QueryTable->EntryContext);
1226 }
1227 else
1228 {
1229 ProcessValues:
1230 /* Loop every value */
1231 i = Value = 0;
1232 while (TRUE)
1233 {
1234 /* Enumerate the keys */
1235 Status = ZwEnumerateValueKey(CurrentKey,
1236 Value,
1237 KeyValueFullInformation,
1238 KeyValueInfo,
1239 (ULONG)InfoSize,
1240 &ResultLength);
1241 if (Status == STATUS_BUFFER_OVERFLOW)
1242 {
1243 /* Normalize the status */
1244 Status = STATUS_BUFFER_TOO_SMALL;
1245 }
1246
1247 /* Check if we found all the entries */
1248 if (Status == STATUS_NO_MORE_ENTRIES)
1249 {
1250 /* Check if this was the first entry and caller needs it */
1251 if (!(Value) &&
1252 (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED))
1253 {
1254 /* Fail */
1255 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1256 }
1257 else
1258 {
1259 /* Otherwise, it's ok */
1260 Status = STATUS_SUCCESS;
1261 }
1262 break;
1263 }
1264
1265 /* Check if enumeration worked */
1266 if (NT_SUCCESS(Status))
1267 {
1268 /* Call the query routine */
1269 ResultLength = (ULONG)InfoSize;
1270 Status = RtlpCallQueryRegistryRoutine(QueryTable,
1271 KeyValueInfo,
1272 &ResultLength,
1273 Context,
1274 Environment);
1275 }
1276
1277 /* Check if the query failed */
1278 if (Status == STATUS_BUFFER_TOO_SMALL)
1279 {
1280 /* Increase allocation size */
1281 BufferSize = ResultLength +
1282 sizeof(ULONG_PTR) +
1283 sizeof(UNICODE_NULL);
1284 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
1285 KeyValueInfo,
1286 BufferSize,
1287 &Status);
1288 if (!KeyValueInfo) break;
1289
1290 /* Update the data */
1291 KeyValueInfo->DataOffset = 0;
1292 InfoSize = BufferSize - sizeof(UNICODE_NULL);
1293
1294 /* Try the value again unless it's been too many times */
1295 if (i++ <= 4) continue;
1296 break;
1297 }
1298
1299 /* Break out if we failed */
1300 if (!NT_SUCCESS(Status)) break;
1301
1302 /* Reset the number of retries and check if we need to delete */
1303 i = 0;
1304 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE)
1305 {
1306 /* Build the name */
1307 RtlInitEmptyUnicodeString(&KeyValueName,
1308 KeyValueInfo->Name,
1309 (USHORT)KeyValueInfo->NameLength);
1310 KeyValueName.Length = KeyValueName.MaximumLength;
1311
1312 /* Delete the key */
1313 Status = ZwDeleteValueKey(CurrentKey, &KeyValueName);
1314 if (NT_SUCCESS(Status)) Value--;
1315 }
1316
1317 /* Go to the next value */
1318 Value++;
1319 }
1320 }
1321
1322 /* Check if we failed anywhere along the road */
1323 if (!NT_SUCCESS(Status)) break;
1324
1325 /* Continue */
1326 QueryTable++;
1327 }
1328
1329 /* Check if we need to close our handle */
1330 if (KeyHandle) RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
1331 if ((CurrentKey) && (CurrentKey != KeyHandle)) ZwClose(CurrentKey);
1332
1333 /* Free our buffer and return status */
1334 RtlpAllocDeallocQueryBuffer(NULL, KeyValueInfo, BufferSize, NULL);
1335 return Status;
1336 }
1337
1338 /* EOF */