:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / 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 */