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