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