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