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