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