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