Changed internal/config.h -> roscfg.h in a number of files.
[reactos.git] / reactos / ntoskrnl / cm / ntfunc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cm/ntfunc.c
5 * PURPOSE: Ntxxx function for registry access
6 * UPDATE HISTORY:
7 */
8
9 #include <ddk/ntddk.h>
10 #include <roscfg.h>
11 #include <internal/ob.h>
12 #include <limits.h>
13 #include <string.h>
14 #include <internal/pool.h>
15 #include <internal/registry.h>
16
17 #define NDEBUG
18 #include <internal/debug.h>
19
20 #include "cm.h"
21
22 extern POBJECT_TYPE CmiKeyType;
23 extern PREGISTRY_FILE CmiVolatileFile;
24
25
26 NTSTATUS STDCALL
27 NtCreateKey(OUT PHANDLE KeyHandle,
28 IN ACCESS_MASK DesiredAccess,
29 IN POBJECT_ATTRIBUTES ObjectAttributes,
30 IN ULONG TitleIndex,
31 IN PUNICODE_STRING Class,
32 IN ULONG CreateOptions,
33 OUT PULONG Disposition)
34 {
35 NTSTATUS Status;
36 PVOID Object;
37 PKEY_OBJECT key;
38 UNICODE_STRING RemainingPath;
39 PWSTR end;
40 // KIRQL OldIrql;
41
42 // DPRINT("NtCreateKey (Name %wZ),KeyHandle=%x,Root=%x\n",
43 // ObjectAttributes->ObjectName,KeyHandle
44 // ,ObjectAttributes->RootDirectory);
45
46 /*FIXME: check for standard handle prefix and adjust objectAttributes accordingly */
47
48 Status = ObFindObject(ObjectAttributes,&Object,&RemainingPath,CmiKeyType);
49 if (!NT_SUCCESS(Status))
50 {
51 return Status;
52 }
53 DPRINT("RP=%wZ\n",&RemainingPath);
54 if ((RemainingPath.Buffer == NULL) || (RemainingPath.Buffer[0] ==0))
55 {
56 /* Fail if the key has been deleted */
57 if (((PKEY_OBJECT)Object)->Flags & KO_MARKED_FOR_DELETE)
58 {
59 ObDereferenceObject(Object);
60 return STATUS_UNSUCCESSFUL;
61 }
62 if (Disposition)
63 *Disposition = REG_OPENED_EXISTING_KEY;
64 Status = ObCreateHandle(PsGetCurrentProcess(),
65 Object,
66 DesiredAccess,
67 FALSE,
68 KeyHandle);
69 DPRINT("Status=%x\n",Status);
70 ObDereferenceObject(Object);
71 return Status;
72 }
73 /* if RemainingPath contains \ : must return error */
74 if((RemainingPath.Buffer[0])=='\\')
75 end = wcschr((RemainingPath.Buffer)+1, '\\');
76 else
77 end = wcschr((RemainingPath.Buffer), '\\');
78 if (end != NULL)
79 {
80 ObDereferenceObject(Object);
81 return STATUS_UNSUCCESSFUL;
82 }
83 /* because NtCreateKey don't create tree */
84
85 DPRINT("NCK %S parent=%x\n",RemainingPath.Buffer,Object);
86 Status = ObCreateObject(KeyHandle,
87 DesiredAccess,
88 NULL,
89 CmiKeyType,
90 (PVOID*)&key);
91
92 if (!NT_SUCCESS(Status))
93 return(Status);
94 key->ParentKey = Object;
95 // if ( (key->ParentKey ==NULL))
96 // key->ParentKey = ObjectAttributes->RootDirectory;
97 if (CreateOptions & REG_OPTION_VOLATILE)
98 key->RegistryFile=CmiVolatileFile;
99 else
100 key->RegistryFile=key->ParentKey->RegistryFile;
101 key->Flags = 0;
102 key->NumberOfSubKeys = 0;
103 key->SizeOfSubKeys = 0;
104 key->SubKeys = NULL;
105 // KeAcquireSpinLock(&key->RegistryFile->RegLock, &OldIrql);
106 /* add key to subkeys of parent if needed */
107 Status = CmiAddSubKey(key->RegistryFile,
108 key->ParentKey,
109 key,
110 RemainingPath.Buffer,
111 RemainingPath.Length,
112 TitleIndex,
113 Class,
114 CreateOptions);
115 key->Name = key->KeyBlock->Name;
116 key->NameSize = key->KeyBlock->NameSize;
117 if (key->RegistryFile == key->ParentKey->RegistryFile)
118 {
119 key->KeyBlock->ParentKeyOffset = key->ParentKey->BlockOffset;
120 key->KeyBlock->SecurityKeyOffset = key->ParentKey->KeyBlock->SecurityKeyOffset;
121 }
122 else
123 {
124 key->KeyBlock->ParentKeyOffset = -1;
125 key->KeyBlock->SecurityKeyOffset = -1;
126 /* this key must rest in memory unless it is deleted */
127 /* , or file is unloaded*/
128 ObReferenceObjectByPointer(key,
129 STANDARD_RIGHTS_REQUIRED,
130 NULL,
131 UserMode);
132 }
133 CmiAddKeyToList(key->ParentKey,key);
134 // KeReleaseSpinLock(&key->RegistryFile->RegLock, OldIrql);
135 ObDereferenceObject(key);
136 ObDereferenceObject(Object);
137 if (Disposition) *Disposition = REG_CREATED_NEW_KEY;
138
139 if (!NT_SUCCESS(Status))
140 {
141 return Status;
142 }
143
144 return STATUS_SUCCESS;
145 }
146
147
148 NTSTATUS STDCALL
149 NtDeleteKey(IN HANDLE KeyHandle)
150 {
151 NTSTATUS Status;
152 PKEY_OBJECT KeyObject;
153
154 /* Verify that the handle is valid and is a registry key */
155 Status = ObReferenceObjectByHandle(KeyHandle,
156 KEY_WRITE,
157 CmiKeyType,
158 UserMode,
159 (PVOID *)&KeyObject,
160 NULL);
161 if (!NT_SUCCESS(Status))
162 {
163 return Status;
164 }
165
166 /* Set the marked for delete bit in the key object */
167 KeyObject->Flags |= KO_MARKED_FOR_DELETE;
168
169 /* Dereference the object */
170 ObDereferenceObject(KeyObject);
171 if(KeyObject->RegistryFile != KeyObject->ParentKey->RegistryFile)
172 ObDereferenceObject(KeyObject);
173 /* close the handle */
174 ObDeleteHandle(PsGetCurrentProcess(),KeyHandle);
175 /* FIXME: I think that ObDeleteHandle should dereference the object */
176 ObDereferenceObject(KeyObject);
177
178
179 return STATUS_SUCCESS;
180 }
181
182
183 NTSTATUS
184 STDCALL
185 NtEnumerateKey (
186 IN HANDLE KeyHandle,
187 IN ULONG Index,
188 IN KEY_INFORMATION_CLASS KeyInformationClass,
189 OUT PVOID KeyInformation,
190 IN ULONG Length,
191 OUT PULONG ResultLength
192 )
193 {
194 NTSTATUS Status;
195 PKEY_OBJECT KeyObject;
196 PREGISTRY_FILE RegistryFile;
197 PKEY_BLOCK KeyBlock, SubKeyBlock;
198 PHASH_TABLE_BLOCK HashTableBlock;
199 PKEY_BASIC_INFORMATION BasicInformation;
200 PKEY_NODE_INFORMATION NodeInformation;
201 PKEY_FULL_INFORMATION FullInformation;
202 PDATA_BLOCK pClassData;
203
204 /* Verify that the handle is valid and is a registry key */
205 Status = ObReferenceObjectByHandle(KeyHandle,
206 KEY_ENUMERATE_SUB_KEYS,
207 CmiKeyType,
208 UserMode,
209 (PVOID *)&KeyObject,
210 NULL);
211 if (!NT_SUCCESS(Status))
212 {
213 return Status;
214 }
215
216 /* Get pointer to KeyBlock */
217 KeyBlock = KeyObject->KeyBlock;
218 RegistryFile = KeyObject->RegistryFile;
219
220 /* Get pointer to SubKey */
221 if(Index >= KeyBlock->NumberOfSubKeys)
222 {
223 if (RegistryFile == CmiVolatileFile)
224 {
225 ObDereferenceObject (KeyObject);
226 return STATUS_NO_MORE_ENTRIES;
227 }
228 else
229 {
230 int i;
231 PKEY_OBJECT CurKey=NULL;
232 /* search volatile keys */
233 for (i=0; i < KeyObject->NumberOfSubKeys; i++)
234 {
235 CurKey=KeyObject->SubKeys[i];
236 if (CurKey->RegistryFile == CmiVolatileFile)
237 {
238 if ( Index-- == KeyObject->NumberOfSubKeys) break;
239 }
240 }
241 if(Index >= KeyBlock->NumberOfSubKeys)
242 {
243 ObDereferenceObject (KeyObject);
244 return STATUS_NO_MORE_ENTRIES;
245 }
246 SubKeyBlock = CurKey->KeyBlock;
247 }
248 }
249 else
250 {
251 HashTableBlock = CmiGetBlock(RegistryFile, KeyBlock->HashTableOffset,NULL);
252 SubKeyBlock = CmiGetKeyFromHashByIndex(RegistryFile,
253 HashTableBlock,
254 Index);
255 }
256 if (SubKeyBlock == NULL)
257 {
258 ObDereferenceObject (KeyObject);
259 return STATUS_NO_MORE_ENTRIES;
260 }
261
262 Status = STATUS_SUCCESS;
263 switch (KeyInformationClass)
264 {
265 case KeyBasicInformation:
266 /* Check size of buffer */
267 if (Length < sizeof(KEY_BASIC_INFORMATION) +
268 (SubKeyBlock->NameSize ) * sizeof(WCHAR))
269 {
270 Status = STATUS_BUFFER_OVERFLOW;
271 }
272 else
273 {
274 /* Fill buffer with requested info */
275 BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
276 BasicInformation->LastWriteTime.u.LowPart = SubKeyBlock->LastWriteTime.dwLowDateTime;
277 BasicInformation->LastWriteTime.u.HighPart = SubKeyBlock->LastWriteTime.dwHighDateTime;
278 BasicInformation->TitleIndex = Index;
279 BasicInformation->NameLength = (SubKeyBlock->NameSize ) * sizeof(WCHAR);
280 mbstowcs(BasicInformation->Name,
281 SubKeyBlock->Name,
282 SubKeyBlock->NameSize*2);
283 // BasicInformation->Name[SubKeyBlock->NameSize] = 0;
284 *ResultLength = sizeof(KEY_BASIC_INFORMATION) +
285 SubKeyBlock->NameSize * sizeof(WCHAR);
286 }
287 break;
288
289 case KeyNodeInformation:
290 /* Check size of buffer */
291 if (Length < sizeof(KEY_NODE_INFORMATION) +
292 (SubKeyBlock->NameSize ) * sizeof(WCHAR) +
293 (SubKeyBlock->ClassSize ))
294 {
295 Status = STATUS_BUFFER_OVERFLOW;
296 }
297 else
298 {
299 /* Fill buffer with requested info */
300 NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
301 NodeInformation->LastWriteTime.u.LowPart = SubKeyBlock->LastWriteTime.dwLowDateTime;
302 NodeInformation->LastWriteTime.u.HighPart = SubKeyBlock->LastWriteTime.dwHighDateTime;
303 NodeInformation->TitleIndex = Index;
304 NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) +
305 SubKeyBlock->NameSize * sizeof(WCHAR);
306 NodeInformation->ClassLength = SubKeyBlock->ClassSize;
307 NodeInformation->NameLength = (SubKeyBlock->NameSize ) * sizeof(WCHAR);
308 mbstowcs(NodeInformation->Name,
309 SubKeyBlock->Name,
310 SubKeyBlock->NameSize*2);
311 // NodeInformation->Name[SubKeyBlock->NameSize] = 0;
312 if (SubKeyBlock->ClassSize != 0)
313 {
314 pClassData=CmiGetBlock(KeyObject->RegistryFile
315 ,SubKeyBlock->ClassNameOffset,NULL);
316 wcsncpy(NodeInformation->Name + SubKeyBlock->NameSize ,
317 (PWCHAR)pClassData->Data,
318 SubKeyBlock->ClassSize);
319 CmiReleaseBlock(RegistryFile, pClassData);
320 }
321 *ResultLength = sizeof(KEY_NODE_INFORMATION) +
322 (SubKeyBlock->NameSize) * sizeof(WCHAR) +
323 (SubKeyBlock->ClassSize );
324 }
325 break;
326
327 case KeyFullInformation:
328 /* check size of buffer */
329 if (Length < sizeof(KEY_FULL_INFORMATION) +
330 SubKeyBlock->ClassSize)
331 {
332 Status = STATUS_BUFFER_OVERFLOW;
333 }
334 else
335 {
336 /* fill buffer with requested info */
337 FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
338 FullInformation->LastWriteTime.u.LowPart = SubKeyBlock->LastWriteTime.dwLowDateTime;
339 FullInformation->LastWriteTime.u.HighPart = SubKeyBlock->LastWriteTime.dwHighDateTime;
340 FullInformation->TitleIndex = Index;
341 FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) -
342 sizeof(WCHAR);
343 FullInformation->ClassLength = SubKeyBlock->ClassSize;
344 FullInformation->SubKeys = SubKeyBlock->NumberOfSubKeys;
345 FullInformation->MaxNameLen =
346 CmiGetMaxNameLength(RegistryFile, SubKeyBlock);
347 FullInformation->MaxClassLen =
348 CmiGetMaxClassLength(RegistryFile, SubKeyBlock);
349 FullInformation->Values = SubKeyBlock->NumberOfValues;
350 FullInformation->MaxValueNameLen =
351 CmiGetMaxValueNameLength(RegistryFile, SubKeyBlock);
352 FullInformation->MaxValueDataLen =
353 CmiGetMaxValueDataLength(RegistryFile, SubKeyBlock);
354 if (SubKeyBlock->ClassSize != 0)
355 {
356 pClassData=CmiGetBlock(KeyObject->RegistryFile
357 ,SubKeyBlock->ClassNameOffset,NULL);
358 wcsncpy(FullInformation->Class,
359 (PWCHAR)pClassData->Data,
360 SubKeyBlock->ClassSize);
361 CmiReleaseBlock(RegistryFile, pClassData);
362 }
363 *ResultLength = sizeof(KEY_FULL_INFORMATION) +
364 SubKeyBlock->ClassSize ;
365 }
366 break;
367 }
368 CmiReleaseBlock(RegistryFile, SubKeyBlock);
369 ObDereferenceObject (KeyObject);
370
371 return Status;
372 }
373
374
375 NTSTATUS
376 STDCALL
377 NtEnumerateValueKey (
378 IN HANDLE KeyHandle,
379 IN ULONG Index,
380 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
381 OUT PVOID KeyValueInformation,
382 IN ULONG Length,
383 OUT PULONG ResultLength
384 )
385 {
386 NTSTATUS Status;
387 PKEY_OBJECT KeyObject;
388 PREGISTRY_FILE RegistryFile;
389 PKEY_BLOCK KeyBlock;
390 PVALUE_BLOCK ValueBlock;
391 PDATA_BLOCK DataBlock;
392 PKEY_VALUE_BASIC_INFORMATION ValueBasicInformation;
393 PKEY_VALUE_PARTIAL_INFORMATION ValuePartialInformation;
394 PKEY_VALUE_FULL_INFORMATION ValueFullInformation;
395
396
397 /* Verify that the handle is valid and is a registry key */
398 Status = ObReferenceObjectByHandle(KeyHandle,
399 KEY_QUERY_VALUE,
400 CmiKeyType,
401 UserMode,
402 (PVOID *)&KeyObject,
403 NULL);
404 if (!NT_SUCCESS(Status))
405 {
406 return Status;
407 }
408
409 /* Get pointer to KeyBlock */
410 KeyBlock = KeyObject->KeyBlock;
411 RegistryFile = KeyObject->RegistryFile;
412
413 /* Get Value block of interest */
414 Status = CmiGetValueFromKeyByIndex(RegistryFile,
415 KeyBlock,
416 Index,
417 &ValueBlock);
418 if (!NT_SUCCESS(Status))
419 {
420 ObDereferenceObject(KeyObject);
421 return Status;
422 }
423 else if (ValueBlock != NULL)
424 {
425 switch (KeyValueInformationClass)
426 {
427 case KeyValueBasicInformation:
428 *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) +
429 (ValueBlock->NameSize + 1) * sizeof(WCHAR);
430 if (Length < *ResultLength)
431 {
432 Status = STATUS_BUFFER_OVERFLOW;
433 }
434 else
435 {
436 ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION)
437 KeyValueInformation;
438 ValueBasicInformation->TitleIndex = 0;
439 ValueBasicInformation->Type = ValueBlock->DataType;
440 ValueBasicInformation->NameLength =
441 (ValueBlock->NameSize + 1) * sizeof(WCHAR);
442 mbstowcs(ValueBasicInformation->Name, ValueBlock->Name
443 ,ValueBlock->NameSize*2);
444 ValueBasicInformation->Name[ValueBlock->NameSize]=0;
445 }
446 break;
447
448 case KeyValuePartialInformation:
449 *ResultLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
450 (ValueBlock->DataSize & LONG_MAX);
451 if (Length < *ResultLength)
452 {
453 Status = STATUS_BUFFER_OVERFLOW;
454 }
455 else
456 {
457 ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
458 KeyValueInformation;
459 ValuePartialInformation->TitleIndex = 0;
460 ValuePartialInformation->Type = ValueBlock->DataType;
461 ValuePartialInformation->DataLength = ValueBlock->DataSize & LONG_MAX;
462 if(ValueBlock->DataSize >0)
463 {
464 DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL);
465 RtlCopyMemory(ValuePartialInformation->Data,
466 DataBlock->Data,
467 ValueBlock->DataSize & LONG_MAX);
468 CmiReleaseBlock(RegistryFile, DataBlock);
469 }
470 else
471 {
472 RtlCopyMemory(ValuePartialInformation->Data,
473 &ValueBlock->DataOffset,
474 ValueBlock->DataSize & LONG_MAX);
475 }
476 DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL);
477 }
478 break;
479
480 case KeyValueFullInformation:
481 *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) +
482 (ValueBlock->NameSize ) * sizeof(WCHAR) + (ValueBlock->DataSize & LONG_MAX);
483 if (Length < *ResultLength)
484 {
485 Status = STATUS_BUFFER_OVERFLOW;
486 }
487 else
488 {
489 ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
490 KeyValueInformation;
491 ValueFullInformation->TitleIndex = 0;
492 ValueFullInformation->Type = ValueBlock->DataType;
493 ValueFullInformation->DataOffset =
494 (DWORD)ValueFullInformation->Name - (DWORD)ValueFullInformation
495 + (ValueBlock->NameSize ) * sizeof(WCHAR);
496 ValueFullInformation->DataOffset =
497 (ValueFullInformation->DataOffset +3) &0xfffffffc;
498 ValueFullInformation->DataLength = ValueBlock->DataSize & LONG_MAX;
499 ValueFullInformation->NameLength =
500 (ValueBlock->NameSize ) * sizeof(WCHAR);
501 mbstowcs(ValueFullInformation->Name, ValueBlock->Name
502 ,ValueBlock->NameSize*2);
503 if(ValueBlock->DataSize >0)
504 {
505 DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL);
506 RtlCopyMemory((char *)(ValueFullInformation)
507 + ValueFullInformation->DataOffset,
508 DataBlock->Data,
509 ValueBlock->DataSize & LONG_MAX);
510 CmiReleaseBlock(RegistryFile, DataBlock);
511 }
512 else
513 {
514 RtlCopyMemory((char *)(ValueFullInformation)
515 + ValueFullInformation->DataOffset,
516 &ValueBlock->DataOffset,
517 ValueBlock->DataSize & LONG_MAX);
518 }
519 }
520 break;
521 }
522 }
523 else
524 {
525 Status = STATUS_UNSUCCESSFUL;
526 }
527 ObDereferenceObject(KeyObject);
528
529 return Status;
530 }
531
532
533 NTSTATUS STDCALL
534 NtFlushKey(IN HANDLE KeyHandle)
535 {
536 NTSTATUS Status;
537 PKEY_OBJECT KeyObject;
538 PREGISTRY_FILE RegistryFile;
539 WCHAR LogName[MAX_PATH];
540 HANDLE FileHandle;
541 // HANDLE FileHandleLog;
542 OBJECT_ATTRIBUTES ObjectAttributes;
543 // KIRQL OldIrql;
544 UNICODE_STRING TmpFileName;
545 int i;
546 LARGE_INTEGER fileOffset;
547 DWORD * pEntDword;
548 /* Verify that the handle is valid and is a registry key */
549 Status = ObReferenceObjectByHandle(KeyHandle,
550 KEY_QUERY_VALUE,
551 CmiKeyType,
552 UserMode,
553 (PVOID *)&KeyObject,
554 NULL);
555 if (!NT_SUCCESS(Status))
556 {
557 return Status;
558 }
559 RegistryFile = KeyObject->RegistryFile;
560 // KeAcquireSpinLock(&RegistryFile->RegLock, &OldIrql);
561 /* then write changed blocks in .log */
562 wcscpy(LogName,RegistryFile->Filename );
563 wcscat(LogName,L".log");
564 RtlInitUnicodeString (&TmpFileName, LogName);
565 InitializeObjectAttributes(&ObjectAttributes,
566 &TmpFileName,
567 0,
568 NULL,
569 NULL);
570 /* BEGIN FIXME : actually (26 November 200) vfatfs.sys can't create new files
571 so we can't create log file
572 Status = ZwCreateFile(&FileHandleLog,
573 FILE_ALL_ACCESS,
574 &ObjectAttributes,
575 NULL, 0, FILE_ATTRIBUTE_NORMAL,
576 0, FILE_SUPERSEDE, 0, NULL, 0);
577 Status = ZwWriteFile(FileHandleLog,
578 0, 0, 0, 0,
579 RegistryFile->HeaderBlock,
580 sizeof(HEADER_BLOCK),
581 0, 0);
582 for (i=0; i < RegistryFile->BlockListSize ; i++)
583 {
584 if( RegistryFile->BlockList[i]->DateModified.dwHighDateTime
585 > RegistryFile->HeaderBlock->DateModified.dwHighDateTime
586 ||( RegistryFile->BlockList[i]->DateModified.dwHighDateTime
587 == RegistryFile->HeaderBlock->DateModified.dwHighDateTime
588 && RegistryFile->BlockList[i]->DateModified.dwLowDateTime
589 > RegistryFile->HeaderBlock->DateModified.dwLowDateTime)
590 )
591 Status = ZwWriteFile(FileHandleLog,
592 0, 0, 0, 0,
593 RegistryFile->BlockList[i],
594 RegistryFile->BlockList[i]->BlockSize ,
595 0, 0);
596 }
597 ZwClose(FileHandleLog);
598 END FIXME*/
599 /* update header of registryfile with Version >VersionOld */
600 /* this allow recover if system crash while updating hove file */
601 RtlInitUnicodeString (&TmpFileName, RegistryFile->Filename);
602 InitializeObjectAttributes(&ObjectAttributes,
603 &TmpFileName,
604 0,
605 NULL,
606 NULL);
607 Status = NtOpenFile(&FileHandle,
608 FILE_ALL_ACCESS,
609 &ObjectAttributes,
610 NULL, 0, 0);
611 RegistryFile->HeaderBlock->Version++;
612
613 Status = ZwWriteFile(FileHandle,
614 0, 0, 0, 0,
615 RegistryFile->HeaderBlock,
616 sizeof(HEADER_BLOCK),
617 0, 0);
618
619 /* update changed blocks in file */
620 fileOffset.u.HighPart = 0;
621 for (i=0; i < RegistryFile->BlockListSize ; i++)
622 {
623 if( RegistryFile->BlockList[i]->DateModified.dwHighDateTime
624 > RegistryFile->HeaderBlock->DateModified.dwHighDateTime
625 ||( RegistryFile->BlockList[i]->DateModified.dwHighDateTime
626 == RegistryFile->HeaderBlock->DateModified.dwHighDateTime
627 && RegistryFile->BlockList[i]->DateModified.dwLowDateTime
628 > RegistryFile->HeaderBlock->DateModified.dwLowDateTime)
629 )
630 {
631 fileOffset.u.LowPart = RegistryFile->BlockList[i]->BlockOffset+4096;
632 Status = NtWriteFile(FileHandle,
633 0, 0, 0, 0,
634 RegistryFile->BlockList[i],
635 RegistryFile->BlockList[i]->BlockSize ,
636 &fileOffset, 0);
637 }
638 }
639 /* change version in header */
640 RegistryFile->HeaderBlock->VersionOld = RegistryFile->HeaderBlock->Version;
641 ZwQuerySystemTime((PTIME) &RegistryFile->HeaderBlock->DateModified);
642 /* calculate checksum */
643 RegistryFile->HeaderBlock->Checksum = 0;
644 pEntDword = (DWORD *) RegistryFile->HeaderBlock;
645 for (i=0 ; i <127 ; i++)
646 {
647 RegistryFile->HeaderBlock->Checksum ^= pEntDword[i];
648 }
649 /* write new header */
650 fileOffset.u.LowPart = 0;
651 Status = ZwWriteFile(FileHandle,
652 0, 0, 0, 0,
653 RegistryFile->HeaderBlock,
654 sizeof(HEADER_BLOCK),
655 &fileOffset, 0);
656 ZwClose(FileHandle);
657 // KeReleaseSpinLock(&RegistryFile->RegLock, OldIrql);
658 return STATUS_SUCCESS;
659 }
660
661
662 NTSTATUS STDCALL
663 NtOpenKey(OUT PHANDLE KeyHandle,
664 IN ACCESS_MASK DesiredAccess,
665 IN POBJECT_ATTRIBUTES ObjectAttributes)
666 {
667 NTSTATUS Status;
668 PVOID Object;
669 UNICODE_STRING RemainingPath;
670
671 RemainingPath.Buffer=NULL;
672 Status = ObFindObject(ObjectAttributes,&Object,&RemainingPath,CmiKeyType);
673 DPRINT("NTOpenKey : after ObFindObject\n");
674 DPRINT("RB.B=%x\n",RemainingPath.Buffer);
675 if(RemainingPath.Buffer != 0 && RemainingPath.Buffer[0] !=0)
676 {
677 DPRINT("NTOpenKey3 : after ObFindObject\n");
678 ObDereferenceObject(Object);
679 DPRINT("RP=%wZ\n",&RemainingPath);
680 return STATUS_UNSUCCESSFUL;
681 }
682 DPRINT("NTOpenKey2 : after ObFindObject\n");
683 /* Fail if the key has been deleted */
684 if (((PKEY_OBJECT)Object)->Flags & KO_MARKED_FOR_DELETE)
685 {
686 ObDereferenceObject(Object);
687 return STATUS_UNSUCCESSFUL;
688 }
689
690 Status = ObCreateHandle(
691 PsGetCurrentProcess(),
692 Object,
693 DesiredAccess,
694 FALSE,
695 KeyHandle
696 );
697 ObDereferenceObject(Object);
698 if (!NT_SUCCESS(Status))
699 {
700 return Status;
701 }
702
703 return STATUS_SUCCESS;
704 }
705
706
707 NTSTATUS
708 STDCALL
709 NtQueryKey (
710 IN HANDLE KeyHandle,
711 IN KEY_INFORMATION_CLASS KeyInformationClass,
712 OUT PVOID KeyInformation,
713 IN ULONG Length,
714 OUT PULONG ResultLength
715 )
716 {
717 NTSTATUS Status;
718 PKEY_OBJECT KeyObject;
719 PREGISTRY_FILE RegistryFile;
720 PKEY_BLOCK KeyBlock;
721 PKEY_BASIC_INFORMATION BasicInformation;
722 PKEY_NODE_INFORMATION NodeInformation;
723 PKEY_FULL_INFORMATION FullInformation;
724 PDATA_BLOCK pClassData;
725
726 /* Verify that the handle is valid and is a registry key */
727 Status = ObReferenceObjectByHandle(KeyHandle,
728 KEY_READ,
729 CmiKeyType,
730 UserMode,
731 (PVOID *)&KeyObject,
732 NULL);
733 if (!NT_SUCCESS(Status))
734 {
735 return Status;
736 }
737
738 /* Get pointer to KeyBlock */
739 KeyBlock = KeyObject->KeyBlock;
740 RegistryFile = KeyObject->RegistryFile;
741
742 Status = STATUS_SUCCESS;
743 switch (KeyInformationClass)
744 {
745 case KeyBasicInformation:
746 /* Check size of buffer */
747 if (Length < sizeof(KEY_BASIC_INFORMATION) +
748 KeyObject->NameSize * sizeof(WCHAR))
749 {
750 Status = STATUS_BUFFER_OVERFLOW;
751 }
752 else
753 {
754 /* Fill buffer with requested info */
755 BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
756 BasicInformation->LastWriteTime.u.LowPart = KeyBlock->LastWriteTime.dwLowDateTime;
757 BasicInformation->LastWriteTime.u.HighPart = KeyBlock->LastWriteTime.dwHighDateTime;
758 BasicInformation->TitleIndex = 0;
759 BasicInformation->NameLength =
760 (KeyObject->NameSize ) * sizeof(WCHAR);
761 mbstowcs(BasicInformation->Name,
762 KeyObject->Name,
763 KeyObject->NameSize*sizeof(WCHAR));
764 *ResultLength = sizeof(KEY_BASIC_INFORMATION) +
765 KeyObject->NameSize * sizeof(WCHAR);
766 }
767 break;
768
769 case KeyNodeInformation:
770 /* Check size of buffer */
771 if (Length < sizeof(KEY_NODE_INFORMATION) +
772 (KeyObject->NameSize ) * sizeof(WCHAR) +
773 KeyBlock->ClassSize )
774 {
775 Status = STATUS_BUFFER_OVERFLOW;
776 }
777 else
778 {
779 /* Fill buffer with requested info */
780 NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
781 NodeInformation->LastWriteTime.u.LowPart = KeyBlock->LastWriteTime.dwLowDateTime;
782 NodeInformation->LastWriteTime.u.HighPart = KeyBlock->LastWriteTime.dwHighDateTime;
783 NodeInformation->TitleIndex = 0;
784 NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) +
785 KeyObject->NameSize * sizeof(WCHAR);
786 NodeInformation->ClassLength = KeyBlock->ClassSize;
787 NodeInformation->NameLength =
788 (KeyObject->NameSize ) * sizeof(WCHAR);
789 mbstowcs(NodeInformation->Name,
790 KeyObject->Name,
791 KeyObject->NameSize*2);
792 if (KeyBlock->ClassSize != 0)
793 {
794 pClassData=CmiGetBlock(KeyObject->RegistryFile
795 ,KeyBlock->ClassNameOffset,NULL);
796 wcsncpy(NodeInformation->Name + (KeyObject->NameSize )*sizeof(WCHAR),
797 (PWCHAR)pClassData->Data,
798 KeyBlock->ClassSize);
799 CmiReleaseBlock(RegistryFile, pClassData);
800 }
801 *ResultLength = sizeof(KEY_NODE_INFORMATION) +
802 (KeyObject->NameSize ) * sizeof(WCHAR) +
803 KeyBlock->ClassSize;
804 }
805 break;
806
807 case KeyFullInformation:
808 /* Check size of buffer */
809 if (Length < sizeof(KEY_FULL_INFORMATION) +
810 KeyBlock->ClassSize )
811 {
812 Status = STATUS_BUFFER_OVERFLOW;
813 }
814 else
815 {
816 /* Fill buffer with requested info */
817 FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
818 FullInformation->LastWriteTime.u.LowPart = KeyBlock->LastWriteTime.dwLowDateTime;
819 FullInformation->LastWriteTime.u.HighPart = KeyBlock->LastWriteTime.dwHighDateTime;
820 FullInformation->TitleIndex = 0;
821 FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) -
822 sizeof(WCHAR);
823 FullInformation->ClassLength = KeyBlock->ClassSize;
824 FullInformation->SubKeys = KeyBlock->NumberOfSubKeys;
825 FullInformation->MaxNameLen =
826 CmiGetMaxNameLength(RegistryFile, KeyBlock);
827 FullInformation->MaxClassLen =
828 CmiGetMaxClassLength(RegistryFile, KeyBlock);
829 FullInformation->Values = KeyBlock->NumberOfValues;
830 FullInformation->MaxValueNameLen =
831 CmiGetMaxValueNameLength(RegistryFile, KeyBlock);
832 FullInformation->MaxValueDataLen =
833 CmiGetMaxValueDataLength(RegistryFile, KeyBlock);
834 if (KeyBlock->ClassSize != 0)
835 {
836 pClassData=CmiGetBlock(KeyObject->RegistryFile
837 ,KeyBlock->ClassNameOffset,NULL);
838 wcsncpy(FullInformation->Class,
839 (PWCHAR)pClassData->Data,
840 KeyBlock->ClassSize);
841 CmiReleaseBlock(RegistryFile, pClassData);
842 }
843 *ResultLength = sizeof(KEY_FULL_INFORMATION) +
844 KeyBlock->ClassSize ;
845 }
846 break;
847 }
848 ObDereferenceObject (KeyObject);
849
850 return Status;
851 }
852
853
854 NTSTATUS
855 STDCALL
856 NtQueryValueKey (
857 IN HANDLE KeyHandle,
858 IN PUNICODE_STRING ValueName,
859 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
860 OUT PVOID KeyValueInformation,
861 IN ULONG Length,
862 OUT PULONG ResultLength
863 )
864 {
865 NTSTATUS Status;
866 PKEY_OBJECT KeyObject;
867 PREGISTRY_FILE RegistryFile;
868 PKEY_BLOCK KeyBlock;
869 PVALUE_BLOCK ValueBlock;
870 PDATA_BLOCK DataBlock;
871 PKEY_VALUE_BASIC_INFORMATION ValueBasicInformation;
872 PKEY_VALUE_PARTIAL_INFORMATION ValuePartialInformation;
873 PKEY_VALUE_FULL_INFORMATION ValueFullInformation;
874 char ValueName2[MAX_PATH];
875
876 wcstombs(ValueName2,ValueName->Buffer,ValueName->Length>>1);
877 ValueName2[ValueName->Length>>1]=0;
878
879 /* Verify that the handle is valid and is a registry key */
880 Status = ObReferenceObjectByHandle(KeyHandle,
881 KEY_QUERY_VALUE,
882 CmiKeyType,
883 UserMode,
884 (PVOID *)&KeyObject,
885 NULL);
886 if (!NT_SUCCESS(Status))
887 {
888 return Status;
889 }
890
891 /* Get pointer to KeyBlock */
892 KeyBlock = KeyObject->KeyBlock;
893 RegistryFile = KeyObject->RegistryFile;
894 /* Get Value block of interest */
895 Status = CmiScanKeyForValue(RegistryFile,
896 KeyBlock,
897 ValueName2,
898 &ValueBlock,NULL);
899 if (!NT_SUCCESS(Status))
900 {
901 ObDereferenceObject(KeyObject);
902 return Status;
903 }
904 else if (ValueBlock != NULL)
905 {
906 switch (KeyValueInformationClass)
907 {
908 case KeyValueBasicInformation:
909 *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) +
910 ValueBlock->NameSize * sizeof(WCHAR);
911 if (Length < *ResultLength)
912 {
913 Status = STATUS_BUFFER_OVERFLOW;
914 }
915 else
916 {
917 ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION)
918 KeyValueInformation;
919 ValueBasicInformation->TitleIndex = 0;
920 ValueBasicInformation->Type = ValueBlock->DataType;
921 ValueBasicInformation->NameLength =
922 (ValueBlock->NameSize + 1) * sizeof(WCHAR);
923 mbstowcs(ValueBasicInformation->Name, ValueBlock->Name,ValueBlock->NameSize*2);
924 ValueBasicInformation->Name[ValueBlock->NameSize]=0;
925 }
926 break;
927
928 case KeyValuePartialInformation:
929 *ResultLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
930 (ValueBlock->DataSize & LONG_MAX);
931 if (Length < *ResultLength)
932 {
933 Status = STATUS_BUFFER_OVERFLOW;
934 }
935 else
936 {
937 ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
938 KeyValueInformation;
939 ValuePartialInformation->TitleIndex = 0;
940 ValuePartialInformation->Type = ValueBlock->DataType;
941 ValuePartialInformation->DataLength = ValueBlock->DataSize & LONG_MAX;
942 if(ValueBlock->DataSize >0)
943 {
944 DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL);
945 RtlCopyMemory(ValuePartialInformation->Data,
946 DataBlock->Data,
947 ValueBlock->DataSize & LONG_MAX);
948 CmiReleaseBlock(RegistryFile, DataBlock);
949 }
950 else
951 {
952 RtlCopyMemory(ValuePartialInformation->Data,
953 &ValueBlock->DataOffset,
954 ValueBlock->DataSize & LONG_MAX);
955 }
956 }
957 break;
958
959 case KeyValueFullInformation:
960 *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION)
961 + (ValueBlock->NameSize -1) * sizeof(WCHAR)
962 + (ValueBlock->DataSize & LONG_MAX);
963 if (Length < *ResultLength)
964 {
965 Status = STATUS_BUFFER_OVERFLOW;
966 }
967 else
968 {
969 ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
970 KeyValueInformation;
971 ValueFullInformation->TitleIndex = 0;
972 ValueFullInformation->Type = ValueBlock->DataType;
973 ValueFullInformation->DataOffset =
974 (DWORD)ValueFullInformation->Name - (DWORD)ValueFullInformation
975 + (ValueBlock->NameSize ) * sizeof(WCHAR);
976 ValueFullInformation->DataOffset =
977 (ValueFullInformation->DataOffset +3) &0xfffffffc;
978 ValueFullInformation->DataLength = ValueBlock->DataSize & LONG_MAX;
979 ValueFullInformation->NameLength =
980 (ValueBlock->NameSize ) * sizeof(WCHAR);
981 mbstowcs(ValueFullInformation->Name, ValueBlock->Name,ValueBlock->NameSize*2);
982 if(ValueBlock->DataSize >0)
983 {
984 DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL);
985 RtlCopyMemory((char *)(ValueFullInformation)
986 + ValueFullInformation->DataOffset,
987 DataBlock->Data,
988 ValueBlock->DataSize & LONG_MAX);
989 CmiReleaseBlock(RegistryFile, DataBlock);
990 }
991 else
992 {
993 RtlCopyMemory((char *)(ValueFullInformation)
994 + ValueFullInformation->DataOffset,
995 &ValueBlock->DataOffset,
996 ValueBlock->DataSize & LONG_MAX);
997 }
998 }
999 break;
1000 }
1001 }
1002 else
1003 {
1004 Status = STATUS_UNSUCCESSFUL;
1005 }
1006 ObDereferenceObject(KeyObject);
1007
1008 return Status;
1009 }
1010
1011
1012 NTSTATUS
1013 STDCALL
1014 NtSetValueKey (
1015 IN HANDLE KeyHandle,
1016 IN PUNICODE_STRING ValueName,
1017 IN ULONG TitleIndex,
1018 IN ULONG Type,
1019 IN PVOID Data,
1020 IN ULONG DataSize
1021 )
1022 {
1023 NTSTATUS Status;
1024 PKEY_OBJECT KeyObject;
1025 PREGISTRY_FILE RegistryFile;
1026 PKEY_BLOCK KeyBlock;
1027 PVALUE_BLOCK ValueBlock;
1028 BLOCK_OFFSET VBOffset;
1029 char ValueName2[MAX_PATH];
1030 PDATA_BLOCK DataBlock, NewDataBlock;
1031 PHEAP_BLOCK pHeap;
1032 // KIRQL OldIrql;
1033
1034 wcstombs(ValueName2,ValueName->Buffer,ValueName->Length>>1);
1035 ValueName2[ValueName->Length>>1]=0;
1036
1037 /* Verify that the handle is valid and is a registry key */
1038 Status = ObReferenceObjectByHandle(KeyHandle,
1039 KEY_SET_VALUE,
1040 CmiKeyType,
1041 UserMode,
1042 (PVOID *)&KeyObject,
1043 NULL);
1044 if (!NT_SUCCESS(Status))
1045 return Status;
1046
1047 /* Get pointer to KeyBlock */
1048 KeyBlock = KeyObject->KeyBlock;
1049 RegistryFile = KeyObject->RegistryFile;
1050 Status = CmiScanKeyForValue(RegistryFile,
1051 KeyBlock,
1052 ValueName2,
1053 &ValueBlock, &VBOffset);
1054 if (!NT_SUCCESS(Status))
1055 {
1056 ObDereferenceObject (KeyObject);
1057 return Status;
1058 }
1059 // KeAcquireSpinLock(&RegistryFile->RegLock, &OldIrql);
1060 if (ValueBlock == NULL)
1061 {
1062 Status = CmiAddValueToKey(RegistryFile,
1063 KeyBlock,
1064 ValueName2,
1065 &ValueBlock,
1066 &VBOffset);
1067 }
1068 if (!NT_SUCCESS(Status))
1069 {
1070 ObDereferenceObject (KeyObject);
1071 return Status;
1072 }
1073 else
1074 {
1075 /* FIXME if datasize <=4 then write in valueblock directly */
1076 if (DataSize <= 4)
1077 {
1078 if (( ValueBlock->DataSize <0 )
1079 && (DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL)))
1080 {
1081 CmiDestroyBlock(RegistryFile, DataBlock, ValueBlock->DataOffset);
1082 }
1083 RtlCopyMemory(&ValueBlock->DataOffset, Data, DataSize);
1084 ValueBlock->DataSize = DataSize | 0x80000000;
1085 ValueBlock->DataType = Type;
1086 memcpy(&ValueBlock->DataOffset, Data, DataSize);
1087 }
1088 /* If new data size is <= current then overwrite current data */
1089 else if (DataSize <= (ValueBlock->DataSize & 0x7fffffff))
1090 {
1091 DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,&pHeap);
1092 RtlCopyMemory(DataBlock->Data, Data, DataSize);
1093 ValueBlock->DataSize = DataSize;
1094 ValueBlock->DataType = Type;
1095 CmiReleaseBlock(RegistryFile, DataBlock);
1096 /* update time of heap */
1097 if(RegistryFile->Filename)
1098 ZwQuerySystemTime((PTIME) &pHeap->DateModified);
1099 }
1100 else
1101 {
1102 BLOCK_OFFSET NewOffset;
1103 /* Destroy current data block and allocate a new one */
1104 if (( ValueBlock->DataSize <0 )
1105 && (DataBlock = CmiGetBlock(RegistryFile, ValueBlock->DataOffset,NULL)))
1106 {
1107 CmiDestroyBlock(RegistryFile, DataBlock, ValueBlock->DataOffset);
1108 }
1109 Status = CmiAllocateBlock(RegistryFile,
1110 (PVOID *)&NewDataBlock,
1111 DataSize,&NewOffset);
1112 RtlCopyMemory(&NewDataBlock->Data[0], Data, DataSize);
1113 ValueBlock->DataSize = DataSize;
1114 ValueBlock->DataType = Type;
1115 CmiReleaseBlock(RegistryFile, NewDataBlock);
1116 ValueBlock->DataOffset = NewOffset;
1117 }
1118 /* update time of heap */
1119 if(RegistryFile->Filename && CmiGetBlock(RegistryFile, VBOffset,&pHeap))
1120 ZwQuerySystemTime((PTIME) &pHeap->DateModified);
1121
1122 }
1123 // KeReleaseSpinLock(&RegistryFile->RegLock, OldIrql);
1124 ObDereferenceObject (KeyObject);
1125
1126 return Status;
1127 }
1128
1129 NTSTATUS
1130 STDCALL
1131 NtDeleteValueKey (
1132 IN HANDLE KeyHandle,
1133 IN PUNICODE_STRING ValueName
1134 )
1135 {
1136 NTSTATUS Status;
1137 PKEY_OBJECT KeyObject;
1138 PREGISTRY_FILE RegistryFile;
1139 PKEY_BLOCK KeyBlock;
1140 char ValueName2[MAX_PATH];
1141 // KIRQL OldIrql;
1142
1143 wcstombs(ValueName2,ValueName->Buffer,ValueName->Length>>1);
1144 ValueName2[ValueName->Length>>1]=0;
1145
1146 /* Verify that the handle is valid and is a registry key */
1147 Status = ObReferenceObjectByHandle(KeyHandle,
1148 KEY_QUERY_VALUE,
1149 CmiKeyType,
1150 UserMode,
1151 (PVOID *)&KeyObject,
1152 NULL);
1153 if (!NT_SUCCESS(Status))
1154 {
1155 return Status;
1156 }
1157
1158 /* Get pointer to KeyBlock */
1159 KeyBlock = KeyObject->KeyBlock;
1160 RegistryFile = KeyObject->RegistryFile;
1161 // KeAcquireSpinLock(&RegistryFile->RegLock, &OldIrql);
1162 Status = CmiDeleteValueFromKey(RegistryFile,
1163 KeyBlock,
1164 ValueName2);
1165 // KeReleaseSpinLock(&RegistryFile->RegLock, OldIrql);
1166 ObDereferenceObject(KeyObject);
1167
1168 return Status;
1169 }
1170
1171 NTSTATUS
1172 STDCALL
1173 NtLoadKey (
1174 PHANDLE KeyHandle,
1175 POBJECT_ATTRIBUTES ObjectAttributes
1176 )
1177 {
1178 return NtLoadKey2(KeyHandle,
1179 ObjectAttributes,
1180 0);
1181 }
1182
1183
1184 NTSTATUS
1185 STDCALL
1186 NtLoadKey2 (
1187 PHANDLE KeyHandle,
1188 POBJECT_ATTRIBUTES ObjectAttributes,
1189 ULONG Unknown3
1190 )
1191 {
1192 UNIMPLEMENTED;
1193 }
1194
1195
1196 NTSTATUS
1197 STDCALL
1198 NtNotifyChangeKey (
1199 IN HANDLE KeyHandle,
1200 IN HANDLE Event,
1201 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
1202 IN PVOID ApcContext OPTIONAL,
1203 OUT PIO_STATUS_BLOCK IoStatusBlock,
1204 IN ULONG CompletionFilter,
1205 IN BOOLEAN Asynchroneous,
1206 OUT PVOID ChangeBuffer,
1207 IN ULONG Length,
1208 IN BOOLEAN WatchSubtree
1209 )
1210 {
1211 UNIMPLEMENTED;
1212 }
1213
1214
1215 NTSTATUS
1216 STDCALL
1217 NtQueryMultipleValueKey (
1218 IN HANDLE KeyHandle,
1219 IN PWVALENT ListOfValuesToQuery,
1220 IN ULONG NumberOfItems,
1221 OUT PVOID MultipleValueInformation,
1222 IN ULONG Length,
1223 OUT PULONG ReturnLength
1224 )
1225 {
1226 UNIMPLEMENTED;
1227 }
1228
1229
1230 NTSTATUS
1231 STDCALL
1232 NtReplaceKey (
1233 IN POBJECT_ATTRIBUTES ObjectAttributes,
1234 IN HANDLE Key,
1235 IN POBJECT_ATTRIBUTES ReplacedObjectAttributes
1236 )
1237 {
1238 UNIMPLEMENTED;
1239 }
1240
1241
1242 NTSTATUS
1243 STDCALL
1244 NtRestoreKey (
1245 IN HANDLE KeyHandle,
1246 IN HANDLE FileHandle,
1247 IN ULONG RestoreFlags
1248 )
1249 {
1250 UNIMPLEMENTED;
1251 }
1252
1253
1254 NTSTATUS
1255 STDCALL
1256 NtSaveKey (
1257 IN HANDLE KeyHandle,
1258 IN HANDLE FileHandle
1259 )
1260 {
1261 UNIMPLEMENTED;
1262 }
1263
1264
1265 NTSTATUS
1266 STDCALL
1267 NtSetInformationKey (
1268 IN HANDLE KeyHandle,
1269 IN CINT KeyInformationClass,
1270 IN PVOID KeyInformation,
1271 IN ULONG KeyInformationLength
1272 )
1273 {
1274 UNIMPLEMENTED;
1275 }
1276
1277
1278 NTSTATUS
1279 STDCALL
1280 NtUnloadKey (
1281 HANDLE KeyHandle
1282 )
1283 {
1284 UNIMPLEMENTED;
1285 }
1286
1287
1288 NTSTATUS
1289 STDCALL
1290 NtInitializeRegistry (
1291 BOOLEAN SetUpBoot
1292 )
1293 {
1294 // UNIMPLEMENTED;
1295 return STATUS_SUCCESS;
1296 }