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