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