xref: /reactos/sdk/tools/mkhive/cmi.c (revision cc439606)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2006 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS hive maker
22  * FILE:            tools/mkhive/cmi.c
23  * PURPOSE:         Registry file manipulation routines
24  * PROGRAMMERS:     Herv� Poussineau
25  *                  Herm�s B�lusca-Ma�to
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #define NDEBUG
31 #include "mkhive.h"
32 
33 /* FUNCTIONS ****************************************************************/
34 
35 PVOID
36 NTAPI
37 CmpAllocate(
38     IN SIZE_T Size,
39     IN BOOLEAN Paged,
40     IN ULONG Tag)
41 {
42     return (PVOID)malloc((size_t)Size);
43 }
44 
45 VOID
46 NTAPI
47 CmpFree(
48     IN PVOID Ptr,
49     IN ULONG Quota)
50 {
51     free(Ptr);
52 }
53 
54 static BOOLEAN
55 NTAPI
56 CmpFileRead(
57     IN PHHIVE RegistryHive,
58     IN ULONG FileType,
59     IN PULONG FileOffset,
60     OUT PVOID Buffer,
61     IN SIZE_T BufferLength)
62 {
63     PCMHIVE CmHive = (PCMHIVE)RegistryHive;
64     FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
65     if (fseek(File, *FileOffset, SEEK_SET) != 0)
66         return FALSE;
67 
68     return (fread(Buffer, 1, BufferLength, File) == BufferLength);
69 }
70 
71 static BOOLEAN
72 NTAPI
73 CmpFileWrite(
74     IN PHHIVE RegistryHive,
75     IN ULONG FileType,
76     IN PULONG FileOffset,
77     IN PVOID Buffer,
78     IN SIZE_T BufferLength)
79 {
80     PCMHIVE CmHive = (PCMHIVE)RegistryHive;
81     FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
82     if (fseek(File, *FileOffset, SEEK_SET) != 0)
83         return FALSE;
84 
85     return (fwrite(Buffer, 1, BufferLength, File) == BufferLength);
86 }
87 
88 static BOOLEAN
89 NTAPI
90 CmpFileSetSize(
91     IN PHHIVE RegistryHive,
92     IN ULONG FileType,
93     IN ULONG FileSize,
94     IN ULONG OldFileSize)
95 {
96     DPRINT1("CmpFileSetSize() unimplemented\n");
97     return FALSE;
98 }
99 
100 static BOOLEAN
101 NTAPI
102 CmpFileFlush(
103     IN PHHIVE RegistryHive,
104     IN ULONG FileType,
105     PLARGE_INTEGER FileOffset,
106     ULONG Length)
107 {
108     PCMHIVE CmHive = (PCMHIVE)RegistryHive;
109     FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
110     return (fflush(File) == 0);
111 }
112 
113 NTSTATUS
114 CmiInitializeHive(
115     IN OUT PCMHIVE Hive,
116     IN PCWSTR Name)
117 {
118     NTSTATUS Status;
119 
120     RtlZeroMemory(Hive, sizeof(*Hive));
121 
122     DPRINT("Hive 0x%p\n", Hive);
123 
124     Status = HvInitialize(&Hive->Hive,
125                           HINIT_CREATE,
126                           HIVE_NOLAZYFLUSH,
127                           HFILE_TYPE_PRIMARY,
128                           0,
129                           CmpAllocate,
130                           CmpFree,
131                           CmpFileSetSize,
132                           CmpFileWrite,
133                           CmpFileRead,
134                           CmpFileFlush,
135                           1,
136                           NULL);
137     if (!NT_SUCCESS(Status))
138     {
139         return Status;
140     }
141 
142     // HACK: See the HACK from r31253
143     if (!CmCreateRootNode(&Hive->Hive, Name))
144     {
145         HvFree(&Hive->Hive);
146         return STATUS_INSUFFICIENT_RESOURCES;
147     }
148 
149     /* Add the new hive to the hive list */
150     InsertTailList(&CmiHiveListHead,
151                    &Hive->HiveList);
152 
153     return STATUS_SUCCESS;
154 }
155 
156 NTSTATUS
157 CmiCreateSecurityKey(
158     IN PHHIVE Hive,
159     IN HCELL_INDEX Cell,
160     IN PUCHAR Descriptor,
161     IN ULONG DescriptorLength)
162 {
163     HCELL_INDEX SecurityCell;
164     PCM_KEY_NODE Node;
165     PCM_KEY_SECURITY Security;
166 
167     Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
168     SecurityCell = HvAllocateCell(Hive,
169                                   FIELD_OFFSET(CM_KEY_SECURITY, Descriptor) +
170                                   DescriptorLength,
171                                   Stable,
172                                   HCELL_NIL);
173     if (SecurityCell == HCELL_NIL)
174     {
175         HvReleaseCell(Hive, Cell);
176         return STATUS_INSUFFICIENT_RESOURCES;
177     }
178 
179     Node->Security = SecurityCell;
180     Security = (PCM_KEY_SECURITY)HvGetCell(Hive, SecurityCell);
181     Security->Signature = CM_KEY_SECURITY_SIGNATURE;
182     Security->ReferenceCount = 1;
183     Security->DescriptorLength = DescriptorLength;
184 
185     RtlMoveMemory(&Security->Descriptor,
186                   Descriptor,
187                   DescriptorLength);
188 
189     Security->Flink = Security->Blink = SecurityCell;
190 
191     HvReleaseCell(Hive, SecurityCell);
192     HvReleaseCell(Hive, Cell);
193 
194     return STATUS_SUCCESS;
195 }
196 
197 static NTSTATUS
198 CmiCreateSubKey(
199     IN PCMHIVE RegistryHive,
200     IN HCELL_INDEX ParentKeyCellOffset,
201     IN PCUNICODE_STRING SubKeyName,
202     IN BOOLEAN VolatileKey,
203     OUT HCELL_INDEX* pNKBOffset)
204 {
205     HCELL_INDEX NKBOffset;
206     PCM_KEY_NODE NewKeyCell;
207     UNICODE_STRING KeyName;
208     HSTORAGE_TYPE Storage;
209 
210     /* Skip leading path separator if present */
211     if (SubKeyName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
212     {
213         KeyName.Buffer = &SubKeyName->Buffer[1];
214         KeyName.Length = KeyName.MaximumLength = SubKeyName->Length - sizeof(WCHAR);
215     }
216     else
217     {
218         KeyName = *SubKeyName;
219     }
220 
221     Storage = (VolatileKey ? Volatile : Stable);
222 
223     NKBOffset = HvAllocateCell(&RegistryHive->Hive,
224                                FIELD_OFFSET(CM_KEY_NODE, Name) +
225                                CmpNameSize(&RegistryHive->Hive, &KeyName),
226                                Storage,
227                                HCELL_NIL);
228     if (NKBOffset == HCELL_NIL)
229     {
230         return STATUS_INSUFFICIENT_RESOURCES;
231     }
232 
233     NewKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, NKBOffset);
234     if (NewKeyCell == NULL)
235     {
236         HvFreeCell(&RegistryHive->Hive, NKBOffset);
237         return STATUS_INSUFFICIENT_RESOURCES;
238     }
239 
240     NewKeyCell->Signature = CM_KEY_NODE_SIGNATURE;
241     NewKeyCell->Flags = (VolatileKey ? KEY_IS_VOLATILE : 0);
242     KeQuerySystemTime(&NewKeyCell->LastWriteTime);
243     NewKeyCell->Parent = ParentKeyCellOffset;
244     NewKeyCell->SubKeyCounts[Stable] = 0;
245     NewKeyCell->SubKeyCounts[Volatile] = 0;
246     NewKeyCell->SubKeyLists[Stable] = HCELL_NIL;
247     NewKeyCell->SubKeyLists[Volatile] = HCELL_NIL;
248     NewKeyCell->ValueList.Count = 0;
249     NewKeyCell->ValueList.List = HCELL_NIL;
250     NewKeyCell->Security = HCELL_NIL;
251     NewKeyCell->Class = HCELL_NIL;
252     NewKeyCell->ClassLength = 0;
253     NewKeyCell->MaxNameLen = 0;
254     NewKeyCell->MaxClassLen = 0;
255     NewKeyCell->MaxValueNameLen = 0;
256     NewKeyCell->MaxValueDataLen = 0;
257     NewKeyCell->NameLength = CmpCopyName(&RegistryHive->Hive, NewKeyCell->Name, &KeyName);
258     if (NewKeyCell->NameLength < KeyName.Length) NewKeyCell->Flags |= KEY_COMP_NAME;
259 
260     /* Inherit the security from the parent */
261     if (ParentKeyCellOffset == HCELL_NIL)
262     {
263         // We are in fact creating a root key.
264         // This is not handled there, but when we
265         // call CmCreateRootNode instead.
266         ASSERT(FALSE);
267     }
268     else
269     {
270         /* Get the parent node */
271         PCM_KEY_NODE ParentKeyCell;
272         ParentKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, ParentKeyCellOffset);
273 
274         if (ParentKeyCell)
275         {
276             /* Inherit the security block of the parent */
277             NewKeyCell->Security = ParentKeyCell->Security;
278             if (NewKeyCell->Security != HCELL_NIL)
279             {
280                 PCM_KEY_SECURITY Security;
281                 Security = (PCM_KEY_SECURITY)HvGetCell(&RegistryHive->Hive, NewKeyCell->Security);
282                 ++Security->ReferenceCount;
283                 HvReleaseCell(&RegistryHive->Hive, NewKeyCell->Security);
284             }
285 
286             HvReleaseCell(&RegistryHive->Hive, ParentKeyCellOffset);
287         }
288     }
289 
290     HvReleaseCell(&RegistryHive->Hive, NKBOffset);
291 
292     *pNKBOffset = NKBOffset;
293     return STATUS_SUCCESS;
294 }
295 
296 NTSTATUS
297 CmiAddSubKey(
298     IN PCMHIVE RegistryHive,
299     IN HCELL_INDEX ParentKeyCellOffset,
300     IN PCUNICODE_STRING SubKeyName,
301     IN BOOLEAN VolatileKey,
302     OUT HCELL_INDEX *pBlockOffset)
303 {
304     PCM_KEY_NODE ParentKeyCell;
305     HCELL_INDEX NKBOffset;
306     NTSTATUS Status;
307 
308     /* Create the new key */
309     Status = CmiCreateSubKey(RegistryHive, ParentKeyCellOffset, SubKeyName, VolatileKey, &NKBOffset);
310     if (!NT_SUCCESS(Status))
311         return Status;
312 
313     /* Mark the parent cell as dirty */
314     HvMarkCellDirty(&RegistryHive->Hive, ParentKeyCellOffset, FALSE);
315 
316     if (!CmpAddSubKey(&RegistryHive->Hive, ParentKeyCellOffset, NKBOffset))
317     {
318         /* FIXME: delete newly created cell */
319         // CmpFreeKeyByCell(&RegistryHive->Hive, NewCell /*NKBOffset*/, FALSE);
320         ASSERT(FALSE);
321         return STATUS_UNSUCCESSFUL;
322     }
323 
324     /* Get the parent node */
325     ParentKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, ParentKeyCellOffset);
326     if (!ParentKeyCell)
327     {
328         /* FIXME: delete newly created cell */
329         return STATUS_UNSUCCESSFUL;
330     }
331     VERIFY_KEY_CELL(ParentKeyCell);
332 
333     /* Update the timestamp */
334     KeQuerySystemTime(&ParentKeyCell->LastWriteTime);
335 
336     /* Check if we need to update name maximum, update it if so */
337     if (ParentKeyCell->MaxNameLen < SubKeyName->Length)
338         ParentKeyCell->MaxNameLen = SubKeyName->Length;
339 
340     /* Release the cell */
341     HvReleaseCell(&RegistryHive->Hive, ParentKeyCellOffset);
342 
343     *pBlockOffset = NKBOffset;
344     return STATUS_SUCCESS;
345 }
346 
347 NTSTATUS
348 CmiAddValueKey(
349     IN PCMHIVE RegistryHive,
350     IN PCM_KEY_NODE Parent,
351     IN ULONG ChildIndex,
352     IN PCUNICODE_STRING ValueName,
353     OUT PCM_KEY_VALUE *pValueCell,
354     OUT HCELL_INDEX *pValueCellOffset)
355 {
356     NTSTATUS Status;
357     HSTORAGE_TYPE Storage;
358     PCM_KEY_VALUE NewValueCell;
359     HCELL_INDEX NewValueCellOffset;
360 
361     Storage = (Parent->Flags & KEY_IS_VOLATILE) ? Volatile : Stable;
362 
363     NewValueCellOffset = HvAllocateCell(&RegistryHive->Hive,
364                                FIELD_OFFSET(CM_KEY_VALUE, Name) +
365                                CmpNameSize(&RegistryHive->Hive, (PUNICODE_STRING)ValueName),
366                                Storage,
367                                HCELL_NIL);
368     if (NewValueCellOffset == HCELL_NIL)
369     {
370         return STATUS_INSUFFICIENT_RESOURCES;
371     }
372 
373     NewValueCell = (PCM_KEY_VALUE)HvGetCell(&RegistryHive->Hive, NewValueCellOffset);
374     if (NewValueCell == NULL)
375     {
376         HvFreeCell(&RegistryHive->Hive, NewValueCellOffset);
377         return STATUS_INSUFFICIENT_RESOURCES;
378     }
379 
380     NewValueCell->Signature = CM_KEY_VALUE_SIGNATURE;
381     NewValueCell->NameLength = CmpCopyName(&RegistryHive->Hive,
382                                            NewValueCell->Name,
383                                            (PUNICODE_STRING)ValueName);
384 
385     /* Check for compressed name */
386     if (NewValueCell->NameLength < ValueName->Length)
387     {
388         /* This is a compressed name */
389         NewValueCell->Flags = VALUE_COMP_NAME;
390     }
391     else
392     {
393         /* No flags to set */
394         NewValueCell->Flags = 0;
395     }
396 
397     NewValueCell->Type = 0;
398     NewValueCell->DataLength = 0;
399     NewValueCell->Data = HCELL_NIL;
400 
401     HvMarkCellDirty(&RegistryHive->Hive, NewValueCellOffset, FALSE);
402 
403     /* Check if we already have a value list */
404     if (Parent->ValueList.Count)
405     {
406         /* Then make sure it's valid and dirty it */
407         ASSERT(Parent->ValueList.List != HCELL_NIL);
408         HvMarkCellDirty(&RegistryHive->Hive, Parent->ValueList.List, FALSE);
409     }
410 
411     /* Add this value cell to the child list */
412     Status = CmpAddValueToList(&RegistryHive->Hive,
413                                NewValueCellOffset,
414                                ChildIndex,
415                                Storage,
416                                &Parent->ValueList);
417 
418     /* If we failed, free the entire cell, including the data */
419     if (!NT_SUCCESS(Status))
420     {
421         /* Overwrite the status with a known one */
422         CmpFreeValue(&RegistryHive->Hive, NewValueCellOffset);
423         Status = STATUS_INSUFFICIENT_RESOURCES;
424     }
425     else
426     {
427         *pValueCell = NewValueCell;
428         *pValueCellOffset = NewValueCellOffset;
429         Status = STATUS_SUCCESS;
430     }
431 
432     return Status;
433 }
434