Use a list to store linked hives.
[reactos.git] / reactos / ntoskrnl / cm / regobj.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cm/regobj.c
5 * PURPOSE: Registry object manipulation routines.
6 * UPDATE HISTORY:
7 */
8
9 #define NTOS_MODE_KERNEL
10 #include <ntos.h>
11 #include <string.h>
12 #include <roscfg.h>
13 #include <internal/ob.h>
14 #include <ntos/minmax.h>
15 #include <reactos/bugcodes.h>
16 #include <rosrtl/string.h>
17
18 #define NDEBUG
19 #include <internal/debug.h>
20
21 #include "cm.h"
22
23
24 static NTSTATUS
25 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
26 PKEY_CELL KeyCell,
27 PUNICODE_STRING TargetPath);
28
29 /* FUNCTONS *****************************************************************/
30
31 NTSTATUS STDCALL
32 CmiObjectParse(PVOID ParsedObject,
33 PVOID *NextObject,
34 PUNICODE_STRING FullPath,
35 PWSTR *Path,
36 ULONG Attributes)
37 {
38 BLOCK_OFFSET BlockOffset;
39 PKEY_OBJECT FoundObject;
40 PKEY_OBJECT ParsedKey;
41 PKEY_CELL SubKeyCell;
42 NTSTATUS Status;
43 PWSTR StartPtr;
44 PWSTR EndPtr;
45 ULONG Length;
46 UNICODE_STRING LinkPath;
47 UNICODE_STRING TargetPath;
48 UNICODE_STRING KeyName;
49 PREGISTRY_HIVE RegistryHive;
50
51 ParsedKey = ParsedObject;
52
53 VERIFY_KEY_OBJECT(ParsedKey);
54
55 *NextObject = NULL;
56
57 if ((*Path) == NULL)
58 {
59 DPRINT("*Path is NULL\n");
60 return STATUS_UNSUCCESSFUL;
61 }
62
63 DPRINT("Path '%S'\n", *Path);
64
65 /* Extract relevant path name */
66 StartPtr = *Path;
67 if (*StartPtr == L'\\')
68 StartPtr++;
69
70 EndPtr = wcschr(StartPtr, L'\\');
71 if (EndPtr != NULL)
72 Length = ((PCHAR)EndPtr - (PCHAR)StartPtr) / sizeof(WCHAR);
73 else
74 Length = wcslen(StartPtr);
75
76
77 KeyName.Length = Length * sizeof(WCHAR);
78 KeyName.MaximumLength = KeyName.Length + sizeof(WCHAR);
79 KeyName.Buffer = ExAllocatePool(NonPagedPool,
80 KeyName.MaximumLength);
81 RtlCopyMemory(KeyName.Buffer,
82 StartPtr,
83 KeyName.Length);
84 KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] = 0;
85
86
87 FoundObject = CmiScanKeyList(ParsedKey,
88 &KeyName,
89 Attributes);
90 if (FoundObject == NULL)
91 {
92 Status = CmiScanForSubKey(ParsedKey->RegistryHive,
93 ParsedKey->KeyCell,
94 &RegistryHive,
95 &SubKeyCell,
96 &BlockOffset,
97 &KeyName,
98 0,
99 Attributes);
100 if (!NT_SUCCESS(Status) || (SubKeyCell == NULL))
101 {
102 RtlFreeUnicodeString(&KeyName);
103 return(STATUS_UNSUCCESSFUL);
104 }
105
106 if ((SubKeyCell->Flags & REG_KEY_LINK_CELL) &&
107 !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)))
108 {
109 RtlInitUnicodeString(&LinkPath, NULL);
110 Status = CmiGetLinkTarget(ParsedKey->RegistryHive,
111 SubKeyCell,
112 &LinkPath);
113 if (NT_SUCCESS(Status))
114 {
115 DPRINT("LinkPath '%wZ'\n", &LinkPath);
116
117 /* build new FullPath for reparsing */
118 TargetPath.MaximumLength = LinkPath.MaximumLength;
119 if (EndPtr != NULL)
120 {
121 TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
122 }
123 TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
124 TargetPath.Buffer = ExAllocatePool(NonPagedPool,
125 TargetPath.MaximumLength);
126 wcscpy(TargetPath.Buffer, LinkPath.Buffer);
127 if (EndPtr != NULL)
128 {
129 wcscat(TargetPath.Buffer, EndPtr);
130 }
131
132 RtlFreeUnicodeString(FullPath);
133 RtlFreeUnicodeString(&LinkPath);
134 FullPath->Length = TargetPath.Length;
135 FullPath->MaximumLength = TargetPath.MaximumLength;
136 FullPath->Buffer = TargetPath.Buffer;
137
138 DPRINT("FullPath '%wZ'\n", FullPath);
139
140 /* reinitialize Path for reparsing */
141 *Path = FullPath->Buffer;
142
143 *NextObject = NULL;
144
145 RtlFreeUnicodeString(&KeyName);
146 return(STATUS_REPARSE);
147 }
148 }
149
150 /* Create new key object and put into linked list */
151 DPRINT("CmiObjectParse: %s\n", Path);
152 Status = ObCreateObject(KernelMode,
153 CmiKeyType,
154 NULL,
155 KernelMode,
156 NULL,
157 sizeof(KEY_OBJECT),
158 0,
159 0,
160 (PVOID*)&FoundObject);
161 if (!NT_SUCCESS(Status))
162 {
163 RtlFreeUnicodeString(&KeyName);
164 return(Status);
165 }
166
167 FoundObject->Flags = 0;
168 FoundObject->KeyCell = SubKeyCell;
169 FoundObject->KeyCellOffset = BlockOffset;
170 FoundObject->RegistryHive = RegistryHive;
171 RtlCreateUnicodeString(&FoundObject->Name,
172 KeyName.Buffer);
173 CmiAddKeyToList(ParsedKey, FoundObject);
174 DPRINT("Created object 0x%x\n", FoundObject);
175 }
176 else
177 {
178 if ((FoundObject->KeyCell->Flags & REG_KEY_LINK_CELL) &&
179 !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)))
180 {
181 DPRINT("Found link\n");
182
183 RtlInitUnicodeString(&LinkPath, NULL);
184 Status = CmiGetLinkTarget(FoundObject->RegistryHive,
185 FoundObject->KeyCell,
186 &LinkPath);
187 if (NT_SUCCESS(Status))
188 {
189 DPRINT("LinkPath '%wZ'\n", &LinkPath);
190
191 /* build new FullPath for reparsing */
192 TargetPath.MaximumLength = LinkPath.MaximumLength;
193 if (EndPtr != NULL)
194 {
195 TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
196 }
197 TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
198 TargetPath.Buffer = ExAllocatePool(NonPagedPool,
199 TargetPath.MaximumLength);
200 wcscpy(TargetPath.Buffer, LinkPath.Buffer);
201 if (EndPtr != NULL)
202 {
203 wcscat(TargetPath.Buffer, EndPtr);
204 }
205
206 RtlFreeUnicodeString(FullPath);
207 RtlFreeUnicodeString(&LinkPath);
208 FullPath->Length = TargetPath.Length;
209 FullPath->MaximumLength = TargetPath.MaximumLength;
210 FullPath->Buffer = TargetPath.Buffer;
211
212 DPRINT("FullPath '%wZ'\n", FullPath);
213
214 /* reinitialize Path for reparsing */
215 *Path = FullPath->Buffer;
216
217 *NextObject = NULL;
218
219 RtlFreeUnicodeString(&KeyName);
220 return(STATUS_REPARSE);
221 }
222 }
223
224 ObReferenceObjectByPointer(FoundObject,
225 STANDARD_RIGHTS_REQUIRED,
226 NULL,
227 UserMode);
228 }
229
230 DPRINT("CmiObjectParse: %s\n", FoundObject->Name);
231
232 *Path = EndPtr;
233
234 VERIFY_KEY_OBJECT(FoundObject);
235
236 *NextObject = FoundObject;
237
238 RtlFreeUnicodeString(&KeyName);
239
240 return(STATUS_SUCCESS);
241 }
242
243
244 NTSTATUS STDCALL
245 CmiObjectCreate(PVOID ObjectBody,
246 PVOID Parent,
247 PWSTR RemainingPath,
248 POBJECT_ATTRIBUTES ObjectAttributes)
249 {
250 PKEY_OBJECT KeyObject = ObjectBody;
251 PWSTR Start;
252
253 KeyObject->ParentKey = Parent;
254 if (RemainingPath)
255 {
256 Start = RemainingPath;
257 if(*Start == L'\\')
258 Start++;
259 RtlCreateUnicodeString(&KeyObject->Name,
260 Start);
261 }
262 else
263 {
264 RtlInitUnicodeString(&KeyObject->Name,
265 NULL);
266 }
267
268 return STATUS_SUCCESS;
269 }
270
271
272 VOID STDCALL
273 CmiObjectDelete(PVOID DeletedObject)
274 {
275 PKEY_OBJECT ParentKeyObject;
276 PKEY_OBJECT KeyObject;
277
278 DPRINT("Delete key object (%p)\n", DeletedObject);
279
280 KeyObject = (PKEY_OBJECT) DeletedObject;
281 ParentKeyObject = KeyObject->ParentKey;
282
283 ObReferenceObject (ParentKeyObject);
284
285 if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject)))
286 {
287 DPRINT1("Key not found in parent list ???\n");
288 }
289
290 RtlFreeUnicodeString(&KeyObject->Name);
291
292 if (KeyObject->Flags & KO_MARKED_FOR_DELETE)
293 {
294 DPRINT("delete really key\n");
295
296 CmiRemoveSubKey(KeyObject->RegistryHive,
297 ParentKeyObject,
298 KeyObject);
299
300 NtQuerySystemTime (&ParentKeyObject->KeyCell->LastWriteTime);
301 CmiMarkBlockDirty (ParentKeyObject->RegistryHive,
302 ParentKeyObject->KeyCellOffset);
303
304 if (!IsNoFileHive (KeyObject->RegistryHive) ||
305 !IsNoFileHive (ParentKeyObject->RegistryHive))
306 {
307 CmiSyncHives ();
308 }
309 }
310
311 ObDereferenceObject (ParentKeyObject);
312
313 if (KeyObject->NumberOfSubKeys)
314 {
315 KEBUGCHECK(REGISTRY_ERROR);
316 }
317
318 if (KeyObject->SizeOfSubKeys)
319 {
320 ExFreePool(KeyObject->SubKeys);
321 }
322 }
323
324
325 NTSTATUS STDCALL
326 CmiObjectSecurity(PVOID ObjectBody,
327 SECURITY_OPERATION_CODE OperationCode,
328 SECURITY_INFORMATION SecurityInformation,
329 PSECURITY_DESCRIPTOR SecurityDescriptor,
330 PULONG BufferLength)
331 {
332 DPRINT1 ("CmiObjectSecurity() called\n");
333
334 return STATUS_SUCCESS;
335 }
336
337
338 NTSTATUS STDCALL
339 CmiObjectQueryName (PVOID ObjectBody,
340 POBJECT_NAME_INFORMATION ObjectNameInfo,
341 ULONG Length,
342 PULONG ReturnLength)
343 {
344 POBJECT_NAME_INFORMATION LocalInfo;
345 PKEY_OBJECT KeyObject;
346 ULONG LocalReturnLength;
347 NTSTATUS Status;
348
349 DPRINT ("CmiObjectQueryName() called\n");
350
351 KeyObject = (PKEY_OBJECT)ObjectBody;
352
353 LocalInfo = ExAllocatePool (NonPagedPool,
354 sizeof(OBJECT_NAME_INFORMATION) +
355 MAX_PATH * sizeof(WCHAR));
356 if (LocalInfo == NULL)
357 return STATUS_INSUFFICIENT_RESOURCES;
358
359 if (KeyObject->ParentKey != KeyObject)
360 {
361 Status = ObQueryNameString (KeyObject->ParentKey,
362 LocalInfo,
363 MAX_PATH * sizeof(WCHAR),
364 &LocalReturnLength);
365 }
366 else
367 {
368 /* KeyObject is the root key */
369 Status = ObQueryNameString (BODY_TO_HEADER(KeyObject)->Parent,
370 LocalInfo,
371 MAX_PATH * sizeof(WCHAR),
372 &LocalReturnLength);
373 }
374
375 if (!NT_SUCCESS (Status))
376 {
377 ExFreePool (LocalInfo);
378 return Status;
379 }
380 DPRINT ("Parent path: %wZ\n", &LocalInfo->Name);
381
382 Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
383 &LocalInfo->Name);
384 ExFreePool (LocalInfo);
385 if (!NT_SUCCESS (Status))
386 return Status;
387
388 Status = RtlAppendUnicodeToString (&ObjectNameInfo->Name,
389 L"\\");
390 if (!NT_SUCCESS (Status))
391 return Status;
392
393 Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
394 &KeyObject->Name);
395 if (NT_SUCCESS (Status))
396 {
397 DPRINT ("Total path: %wZ\n", &ObjectNameInfo->Name);
398 }
399
400 return Status;
401 }
402
403
404 VOID
405 CmiAddKeyToList(PKEY_OBJECT ParentKey,
406 PKEY_OBJECT NewKey)
407 {
408 KIRQL OldIrql;
409
410 DPRINT("ParentKey %.08x\n", ParentKey);
411
412 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
413
414 if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys)
415 {
416 PKEY_OBJECT *tmpSubKeys = ExAllocatePool(NonPagedPool,
417 (ParentKey->NumberOfSubKeys + 1) * sizeof(ULONG));
418
419 if (ParentKey->NumberOfSubKeys > 0)
420 {
421 RtlCopyMemory (tmpSubKeys,
422 ParentKey->SubKeys,
423 ParentKey->NumberOfSubKeys * sizeof(ULONG));
424 }
425
426 if (ParentKey->SubKeys)
427 ExFreePool(ParentKey->SubKeys);
428
429 ParentKey->SubKeys = tmpSubKeys;
430 ParentKey->SizeOfSubKeys = ParentKey->NumberOfSubKeys + 1;
431 }
432
433 /* FIXME: Please maintain the list in alphabetic order */
434 /* to allow a dichotomic search */
435 ParentKey->SubKeys[ParentKey->NumberOfSubKeys++] = NewKey;
436
437 DPRINT("Reference parent key: 0x%x\n", ParentKey);
438
439 ObReferenceObjectByPointer(ParentKey,
440 STANDARD_RIGHTS_REQUIRED,
441 NULL,
442 UserMode);
443 NewKey->ParentKey = ParentKey;
444 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
445 }
446
447
448 NTSTATUS
449 CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove)
450 {
451 PKEY_OBJECT ParentKey;
452 KIRQL OldIrql;
453 DWORD Index;
454
455 ParentKey = KeyToRemove->ParentKey;
456 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
457 /* FIXME: If list maintained in alphabetic order, use dichotomic search */
458 for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++)
459 {
460 if (ParentKey->SubKeys[Index] == KeyToRemove)
461 {
462 if (Index < ParentKey->NumberOfSubKeys-1)
463 RtlMoveMemory(&ParentKey->SubKeys[Index],
464 &ParentKey->SubKeys[Index + 1],
465 (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT));
466 ParentKey->NumberOfSubKeys--;
467 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
468
469 DPRINT("Dereference parent key: 0x%x\n", ParentKey);
470
471 ObDereferenceObject(ParentKey);
472 return STATUS_SUCCESS;
473 }
474 }
475 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
476
477 return STATUS_UNSUCCESSFUL;
478 }
479
480
481 PKEY_OBJECT
482 CmiScanKeyList(PKEY_OBJECT Parent,
483 PUNICODE_STRING KeyName,
484 ULONG Attributes)
485 {
486 PKEY_OBJECT CurKey;
487 KIRQL OldIrql;
488 ULONG Index;
489
490 DPRINT("Scanning key list for: %wZ (Parent: %wZ)\n",
491 KeyName, &Parent->Name);
492
493 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
494 /* FIXME: if list maintained in alphabetic order, use dichotomic search */
495 for (Index=0; Index < Parent->NumberOfSubKeys; Index++)
496 {
497 CurKey = Parent->SubKeys[Index];
498 if (Attributes & OBJ_CASE_INSENSITIVE)
499 {
500 if ((KeyName->Length == CurKey->Name.Length)
501 && (_wcsicmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
502 {
503 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
504 return CurKey;
505 }
506 }
507 else
508 {
509 if ((KeyName->Length == CurKey->Name.Length)
510 && (wcscmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
511 {
512 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
513 return CurKey;
514 }
515 }
516 }
517 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
518
519 return NULL;
520 }
521
522
523 static NTSTATUS
524 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
525 PKEY_CELL KeyCell,
526 PUNICODE_STRING TargetPath)
527 {
528 UNICODE_STRING LinkName = ROS_STRING_INITIALIZER(L"SymbolicLinkValue");
529 PVALUE_CELL ValueCell;
530 PDATA_CELL DataCell;
531 NTSTATUS Status;
532
533 DPRINT("CmiGetLinkTarget() called\n");
534
535 /* Get Value block of interest */
536 Status = CmiScanKeyForValue(RegistryHive,
537 KeyCell,
538 &LinkName,
539 &ValueCell,
540 NULL);
541 if (!NT_SUCCESS(Status))
542 {
543 DPRINT1("CmiScanKeyForValue() failed (Status %lx)\n", Status);
544 return(Status);
545 }
546
547 if (ValueCell->DataType != REG_LINK)
548 {
549 DPRINT1("Type != REG_LINK\n!");
550 return(STATUS_UNSUCCESSFUL);
551 }
552
553 if (TargetPath->Buffer == NULL && TargetPath->MaximumLength == 0)
554 {
555 TargetPath->Length = 0;
556 TargetPath->MaximumLength = ValueCell->DataSize + sizeof(WCHAR);
557 TargetPath->Buffer = ExAllocatePool(NonPagedPool,
558 TargetPath->MaximumLength);
559 }
560
561 TargetPath->Length = min(TargetPath->MaximumLength - sizeof(WCHAR),
562 (ULONG) ValueCell->DataSize);
563
564 if (ValueCell->DataSize > 0)
565 {
566 DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
567 RtlCopyMemory(TargetPath->Buffer,
568 DataCell->Data,
569 TargetPath->Length);
570 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
571 }
572 else
573 {
574 RtlCopyMemory(TargetPath->Buffer,
575 &ValueCell->DataOffset,
576 TargetPath->Length);
577 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
578 }
579
580 DPRINT("TargetPath '%wZ'\n", TargetPath);
581
582 return(STATUS_SUCCESS);
583 }
584
585 /* EOF */