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