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