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