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