update for HEAD-2003021201
[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 #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 StartPtr;
49   PWSTR EndPtr;
50   ULONG Length;
51   UNICODE_STRING LinkPath;
52   UNICODE_STRING TargetPath;
53
54   ParsedKey = ParsedObject;
55
56   VERIFY_KEY_OBJECT(ParsedKey);
57
58   *NextObject = NULL;
59
60   if ((*Path) == NULL)
61     {
62       DPRINT("*Path is NULL\n");
63       return STATUS_UNSUCCESSFUL;
64     }
65
66   DPRINT("Path '%S'\n", *Path);
67
68   /* Extract relevant path name */
69   StartPtr = *Path;
70   if (*StartPtr == L'\\')
71     StartPtr++;
72
73   EndPtr = wcschr(StartPtr, L'\\');
74   if (EndPtr != NULL)
75     Length = ((PCHAR)EndPtr - (PCHAR)StartPtr) / sizeof(WCHAR);
76   else
77     Length = wcslen(StartPtr);
78
79   wcstombs(cPath, StartPtr, Length);
80   cPath[Length] = 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           return(STATUS_UNSUCCESSFUL);
96         }
97
98       if ((SubKeyCell->Type == REG_LINK_KEY_CELL_TYPE) &&
99           !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL) /*(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 (EndPtr != NULL)
112                 {
113                   TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
114                 }
115               TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
116               TargetPath.Buffer = ExAllocatePool(NonPagedPool,
117                                                  TargetPath.MaximumLength);
118               wcscpy(TargetPath.Buffer, LinkPath.Buffer);
119               if (EndPtr != NULL)
120                 {
121                   wcscat(TargetPath.Buffer, EndPtr);
122                 }
123
124               RtlFreeUnicodeString(FullPath);
125               RtlFreeUnicodeString(&LinkPath);
126               FullPath->Length = TargetPath.Length;
127               FullPath->MaximumLength = TargetPath.MaximumLength;
128               FullPath->Buffer = TargetPath.Buffer;
129
130               DPRINT("FullPath '%wZ'\n", FullPath);
131
132               /* reinitialize Path for reparsing */
133               *Path = FullPath->Buffer;
134
135               *NextObject = NULL;
136               return(STATUS_REPARSE);
137             }
138         }
139
140       /* Create new key object and put into linked list */
141       DPRINT("CmiObjectParse: %s\n", cPath);
142       Status = ObCreateObject(NULL,
143                               STANDARD_RIGHTS_REQUIRED,
144                               NULL,
145                               CmiKeyType,
146                               (PVOID*)&FoundObject);
147       if (!NT_SUCCESS(Status))
148         {
149           return(Status);
150         }
151
152       FoundObject->Flags = 0;
153       FoundObject->Name = SubKeyCell->Name;
154       FoundObject->NameSize = SubKeyCell->NameSize;
155       FoundObject->KeyCell = SubKeyCell;
156       FoundObject->BlockOffset = BlockOffset;
157       FoundObject->RegistryHive = ParsedKey->RegistryHive;
158       CmiAddKeyToList(ParsedKey, FoundObject);
159       DPRINT("Created object 0x%x\n", FoundObject);
160     }
161   else
162     {
163       if ((FoundObject->KeyCell->Type == REG_LINK_KEY_CELL_TYPE) &&
164           !((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)/*(end == NULL)*/))
165         {
166           DPRINT("Found link\n");
167
168           RtlInitUnicodeString(&LinkPath, NULL);
169           Status = CmiGetLinkTarget(FoundObject->RegistryHive,
170                                     FoundObject->KeyCell,
171                                     &LinkPath);
172           if (NT_SUCCESS(Status))
173             {
174               DPRINT("LinkPath '%wZ'\n", &LinkPath);
175
176               /* build new FullPath for reparsing */
177               TargetPath.MaximumLength = LinkPath.MaximumLength;
178               if (EndPtr != NULL)
179                 {
180                   TargetPath.MaximumLength += (wcslen(EndPtr) * 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 (EndPtr != NULL)
187                 {
188                   wcscat(TargetPath.Buffer, EndPtr);
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 #ifndef WIN32_REGDBG
213   DPRINT("CmiObjectParse: %s\n", FoundObject->Name);
214 #else
215   {
216       char buffer[_BUFFER_LEN];
217       memset(buffer, 0, _BUFFER_LEN);
218       strncpy(buffer, FoundObject->Name, min(FoundObject->NameSize, _BUFFER_LEN - 1));
219       DPRINT("CmiObjectParse: %s\n", buffer);
220   }
221 #endif
222
223   *Path = EndPtr;
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
282       CmiRemoveSubKey(KeyObject->RegistryHive,
283                       KeyObject->ParentKey,
284                       KeyObject);
285
286       if (IsPermanentHive(KeyObject->RegistryHive))
287         CmiSyncHives();
288     }
289   else
290     {
291       CmiReleaseBlock(KeyObject->RegistryHive, KeyObject->KeyCell);
292     }
293 }
294
295
296 VOID
297 CmiAddKeyToList(PKEY_OBJECT ParentKey,
298                 PKEY_OBJECT NewKey)
299 {
300   KIRQL OldIrql;
301
302   DPRINT("ParentKey %.08x\n", ParentKey);
303
304   KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
305
306   if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys)
307     {
308       PKEY_OBJECT *tmpSubKeys = ExAllocatePool(NonPagedPool,
309         (ParentKey->NumberOfSubKeys + 1) * sizeof(DWORD));
310
311       if (ParentKey->NumberOfSubKeys > 0)
312         {
313           memcpy(tmpSubKeys,
314                  ParentKey->SubKeys,
315                  ParentKey->NumberOfSubKeys * sizeof(DWORD));
316         }
317
318       if (ParentKey->SubKeys)
319         ExFreePool(ParentKey->SubKeys);
320
321       ParentKey->SubKeys = tmpSubKeys;
322       ParentKey->SizeOfSubKeys = ParentKey->NumberOfSubKeys + 1;
323     }
324
325   /* FIXME: Please maintain the list in alphabetic order */
326   /*      to allow a dichotomic search */
327   ParentKey->SubKeys[ParentKey->NumberOfSubKeys++] = NewKey;
328
329   DPRINT("Reference parent key: 0x%x\n", ParentKey);
330
331   ObReferenceObjectByPointer(ParentKey,
332                 STANDARD_RIGHTS_REQUIRED,
333                 NULL,
334                 UserMode);
335   NewKey->ParentKey = ParentKey;
336   KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
337 }
338
339
340 NTSTATUS
341 CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove)
342 {
343   PKEY_OBJECT ParentKey;
344   KIRQL OldIrql;
345   DWORD Index;
346
347   ParentKey = KeyToRemove->ParentKey;
348   KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
349   /* FIXME: If list maintained in alphabetic order, use dichotomic search */
350   for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++)
351     {
352       if (ParentKey->SubKeys[Index] == KeyToRemove)
353         {
354           if (Index < ParentKey->NumberOfSubKeys-1)
355             RtlMoveMemory(&ParentKey->SubKeys[Index],
356                           &ParentKey->SubKeys[Index + 1],
357                           (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT));
358           ParentKey->NumberOfSubKeys--;
359           KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
360
361           DPRINT("Dereference parent key: 0x%x\n", ParentKey);
362         
363           ObDereferenceObject(ParentKey);
364           return STATUS_SUCCESS;
365         }
366     }
367   KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
368
369   return STATUS_UNSUCCESSFUL;
370 }
371
372
373 PKEY_OBJECT
374 CmiScanKeyList(PKEY_OBJECT Parent,
375                PCHAR KeyName,
376                ULONG Attributes)
377 {
378   PKEY_OBJECT CurKey;
379   KIRQL OldIrql;
380   WORD NameSize;
381   DWORD Index;
382
383 #ifndef WIN32_REGDBG
384   DPRINT("Scanning key list for: %s (Parent: %s)\n",
385     KeyName, Parent->Name);
386 #else
387   {
388       char buffer[_BUFFER_LEN];
389       memset(buffer, 0, _BUFFER_LEN);
390       strncpy(buffer, Parent->Name, min(Parent->NameSize, _BUFFER_LEN - 1));
391       DPRINT("Scanning key list for: %s (Parent: %s)\n", KeyName, buffer);
392   }
393 #endif
394
395   NameSize = strlen(KeyName);
396   KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
397   /* FIXME: if list maintained in alphabetic order, use dichotomic search */
398   for (Index=0; Index < Parent->NumberOfSubKeys; Index++)
399     {
400       CurKey = Parent->SubKeys[Index];
401       if (Attributes & OBJ_CASE_INSENSITIVE)
402         {
403           if ((NameSize == CurKey->NameSize)
404               && (_strnicmp(KeyName, CurKey->Name, NameSize) == 0))
405             {
406               KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
407               return CurKey;
408             }
409         }
410       else
411         {
412           if ((NameSize == CurKey->NameSize)
413               && (strncmp(KeyName,CurKey->Name,NameSize) == 0))
414             {
415               KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
416               return CurKey;
417             }
418         }
419     }
420   KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
421   
422   return NULL;
423 }
424
425
426 static NTSTATUS
427 CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
428                  PKEY_CELL KeyCell,
429                  PUNICODE_STRING TargetPath)
430 {
431   UNICODE_STRING LinkName = UNICODE_STRING_INITIALIZER(L"SymbolicLinkValue");
432   PVALUE_CELL ValueCell;
433   PDATA_CELL DataCell;
434   NTSTATUS Status;
435
436   DPRINT("CmiGetLinkTarget() called\n");
437
438   /* Get Value block of interest */
439   Status = CmiScanKeyForValue(RegistryHive,
440                               KeyCell,
441                               &LinkName,
442                               &ValueCell,
443                               NULL);
444   if (!NT_SUCCESS(Status))
445     {
446       DPRINT1("CmiScanKeyForValue() failed (Status %lx)\n", Status);
447       return(Status);
448     }
449
450   if (ValueCell->DataType != REG_LINK)
451     {
452       DPRINT1("Type != REG_LINK\n!");
453       return(STATUS_UNSUCCESSFUL);
454     }
455
456   if (TargetPath->Buffer == NULL && TargetPath->MaximumLength == 0)
457     {
458       TargetPath->Length = 0;
459       TargetPath->MaximumLength = ValueCell->DataSize + sizeof(WCHAR);
460       TargetPath->Buffer = ExAllocatePool(NonPagedPool,
461                                           TargetPath->MaximumLength);
462     }
463
464   TargetPath->Length = min(TargetPath->MaximumLength - sizeof(WCHAR),
465                            (ULONG) ValueCell->DataSize);
466
467   if (ValueCell->DataSize > 0)
468     {
469       DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
470       RtlCopyMemory(TargetPath->Buffer,
471                     DataCell->Data,
472                     TargetPath->Length);
473       TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
474       CmiReleaseBlock(RegistryHive, DataCell);
475     }
476   else
477     {
478       RtlCopyMemory(TargetPath->Buffer,
479                     &ValueCell->DataOffset,
480                     TargetPath->Length);
481       TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
482     }
483
484   DPRINT("TargetPath '%wZ'\n", TargetPath);
485
486   return(STATUS_SUCCESS);
487 }
488
489 /* EOF */