2003-06-07 Casper S. Hornstrup <chorns@users.sourceforge.net>
[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", cPath);
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 if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject)))
276 {
277 DPRINT1("Key not found in parent list ???\n");
278 }
279
280 RtlFreeUnicodeString(&KeyObject->Name);
281
282 if (KeyObject->Flags & KO_MARKED_FOR_DELETE)
283 {
284 DPRINT("delete really key\n");
285
286 CmiRemoveSubKey(KeyObject->RegistryHive,
287 KeyObject->ParentKey,
288 KeyObject);
289
290 if (!IsNoFileHive(KeyObject->RegistryHive))
291 {
292 CmiSyncHives();
293 }
294 }
295 }
296
297
298 NTSTATUS STDCALL
299 CmiObjectSecurity(PVOID ObjectBody,
300 SECURITY_OPERATION_CODE OperationCode,
301 SECURITY_INFORMATION SecurityInformation,
302 PSECURITY_DESCRIPTOR SecurityDescriptor,
303 PULONG BufferLength)
304 {
305 DPRINT1 ("CmiObjectSecurity() called\n");
306
307 return STATUS_SUCCESS;
308 }
309
310
311 NTSTATUS STDCALL
312 CmiObjectQueryName (PVOID ObjectBody,
313 POBJECT_NAME_INFORMATION ObjectNameInfo,
314 ULONG Length,
315 PULONG ReturnLength)
316 {
317 POBJECT_NAME_INFORMATION LocalInfo;
318 PKEY_OBJECT KeyObject;
319 ULONG LocalReturnLength;
320 NTSTATUS Status;
321
322 DPRINT ("CmiObjectQueryName() called\n");
323
324 KeyObject = (PKEY_OBJECT)ObjectBody;
325
326 LocalInfo = ExAllocatePool (NonPagedPool,
327 sizeof(OBJECT_NAME_INFORMATION) +
328 MAX_PATH * sizeof(WCHAR));
329 if (LocalInfo == NULL)
330 return STATUS_INSUFFICIENT_RESOURCES;
331
332 if (KeyObject->ParentKey != KeyObject)
333 {
334 Status = ObQueryNameString (KeyObject->ParentKey,
335 LocalInfo,
336 MAX_PATH * sizeof(WCHAR),
337 &LocalReturnLength);
338 }
339 else
340 {
341 /* KeyObject is the root key */
342 Status = ObQueryNameString (BODY_TO_HEADER(KeyObject)->Parent,
343 LocalInfo,
344 MAX_PATH * sizeof(WCHAR),
345 &LocalReturnLength);
346 }
347
348 if (!NT_SUCCESS (Status))
349 {
350 ExFreePool (LocalInfo);
351 return Status;
352 }
353 DPRINT ("Parent path: %wZ\n", &LocalInfo->Name);
354
355 Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
356 &LocalInfo->Name);
357 ExFreePool (LocalInfo);
358 if (!NT_SUCCESS (Status))
359 return Status;
360
361 Status = RtlAppendUnicodeToString (&ObjectNameInfo->Name,
362 L"\\");
363 if (!NT_SUCCESS (Status))
364 return Status;
365
366 Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
367 &KeyObject->Name);
368 if (NT_SUCCESS (Status))
369 {
370 DPRINT ("Total path: %wZ\n", &ObjectNameInfo->Name);
371 }
372
373 return Status;
374 }
375
376
377 VOID
378 CmiAddKeyToList(PKEY_OBJECT ParentKey,
379 PKEY_OBJECT NewKey)
380 {
381 KIRQL OldIrql;
382
383 DPRINT("ParentKey %.08x\n", ParentKey);
384
385 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
386
387 if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys)
388 {
389 PKEY_OBJECT *tmpSubKeys = ExAllocatePool(NonPagedPool,
390 (ParentKey->NumberOfSubKeys + 1) * sizeof(ULONG));
391
392 if (ParentKey->NumberOfSubKeys > 0)
393 {
394 RtlCopyMemory (tmpSubKeys,
395 ParentKey->SubKeys,
396 ParentKey->NumberOfSubKeys * sizeof(ULONG));
397 }
398
399 if (ParentKey->SubKeys)
400 ExFreePool(ParentKey->SubKeys);
401
402 ParentKey->SubKeys = tmpSubKeys;
403 ParentKey->SizeOfSubKeys = ParentKey->NumberOfSubKeys + 1;
404 }
405
406 /* FIXME: Please maintain the list in alphabetic order */
407 /* to allow a dichotomic search */
408 ParentKey->SubKeys[ParentKey->NumberOfSubKeys++] = NewKey;
409
410 DPRINT("Reference parent key: 0x%x\n", ParentKey);
411
412 ObReferenceObjectByPointer(ParentKey,
413 STANDARD_RIGHTS_REQUIRED,
414 NULL,
415 UserMode);
416 NewKey->ParentKey = ParentKey;
417 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
418 }
419
420
421 NTSTATUS
422 CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove)
423 {
424 PKEY_OBJECT ParentKey;
425 KIRQL OldIrql;
426 DWORD Index;
427
428 ParentKey = KeyToRemove->ParentKey;
429 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
430 /* FIXME: If list maintained in alphabetic order, use dichotomic search */
431 for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++)
432 {
433 if (ParentKey->SubKeys[Index] == KeyToRemove)
434 {
435 if (Index < ParentKey->NumberOfSubKeys-1)
436 RtlMoveMemory(&ParentKey->SubKeys[Index],
437 &ParentKey->SubKeys[Index + 1],
438 (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT));
439 ParentKey->NumberOfSubKeys--;
440 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
441
442 DPRINT("Dereference parent key: 0x%x\n", ParentKey);
443
444 ObDereferenceObject(ParentKey);
445 return STATUS_SUCCESS;
446 }
447 }
448 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
449
450 return STATUS_UNSUCCESSFUL;
451 }
452
453
454 PKEY_OBJECT
455 CmiScanKeyList(PKEY_OBJECT Parent,
456 PUNICODE_STRING KeyName,
457 ULONG Attributes)
458 {
459 PKEY_OBJECT CurKey;
460 KIRQL OldIrql;
461 ULONG Index;
462
463 DPRINT("Scanning key list for: %wZ (Parent: %wZ)\n",
464 KeyName, &Parent->Name);
465
466 KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
467 /* FIXME: if list maintained in alphabetic order, use dichotomic search */
468 for (Index=0; Index < Parent->NumberOfSubKeys; Index++)
469 {
470 CurKey = Parent->SubKeys[Index];
471 if (Attributes & OBJ_CASE_INSENSITIVE)
472 {
473 if ((KeyName->Length == CurKey->Name.Length)
474 && (_wcsicmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
475 {
476 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
477 return CurKey;
478 }
479 }
480 else
481 {
482 if ((KeyName->Length == CurKey->Name.Length)
483 && (wcscmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
484 {
485 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
486 return CurKey;
487 }
488 }
489 }
490 KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
491
492 return NULL;
493 }
494
495
496 static NTSTATUS
497 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
498 PKEY_CELL KeyCell,
499 PUNICODE_STRING TargetPath)
500 {
501 UNICODE_STRING LinkName = UNICODE_STRING_INITIALIZER(L"SymbolicLinkValue");
502 PVALUE_CELL ValueCell;
503 PDATA_CELL DataCell;
504 NTSTATUS Status;
505
506 DPRINT("CmiGetLinkTarget() called\n");
507
508 /* Get Value block of interest */
509 Status = CmiScanKeyForValue(RegistryHive,
510 KeyCell,
511 &LinkName,
512 &ValueCell,
513 NULL);
514 if (!NT_SUCCESS(Status))
515 {
516 DPRINT1("CmiScanKeyForValue() failed (Status %lx)\n", Status);
517 return(Status);
518 }
519
520 if (ValueCell->DataType != REG_LINK)
521 {
522 DPRINT1("Type != REG_LINK\n!");
523 return(STATUS_UNSUCCESSFUL);
524 }
525
526 if (TargetPath->Buffer == NULL && TargetPath->MaximumLength == 0)
527 {
528 TargetPath->Length = 0;
529 TargetPath->MaximumLength = ValueCell->DataSize + sizeof(WCHAR);
530 TargetPath->Buffer = ExAllocatePool(NonPagedPool,
531 TargetPath->MaximumLength);
532 }
533
534 TargetPath->Length = min(TargetPath->MaximumLength - sizeof(WCHAR),
535 (ULONG) ValueCell->DataSize);
536
537 if (ValueCell->DataSize > 0)
538 {
539 DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
540 RtlCopyMemory(TargetPath->Buffer,
541 DataCell->Data,
542 TargetPath->Length);
543 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
544 }
545 else
546 {
547 RtlCopyMemory(TargetPath->Buffer,
548 &ValueCell->DataOffset,
549 TargetPath->Length);
550 TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
551 }
552
553 DPRINT("TargetPath '%wZ'\n", TargetPath);
554
555 return(STATUS_SUCCESS);
556 }
557
558 /* EOF */