Add CPropertyPageImpl that allows us to make property pages object oriented
[reactos.git] / sdk / tools / mkhive / cmi.c
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 * PROGRAMMER: Hervé Poussineau
25 */
26
27 #define NDEBUG
28 #include "mkhive.h"
29
30 PVOID
31 NTAPI
32 CmpAllocate(
33 IN SIZE_T Size,
34 IN BOOLEAN Paged,
35 IN ULONG Tag)
36 {
37 return (PVOID)malloc((size_t)Size);
38 }
39
40 VOID
41 NTAPI
42 CmpFree(
43 IN PVOID Ptr,
44 IN ULONG Quota)
45 {
46 free(Ptr);
47 }
48
49 static BOOLEAN
50 NTAPI
51 CmpFileRead(
52 IN PHHIVE RegistryHive,
53 IN ULONG FileType,
54 IN PULONG FileOffset,
55 OUT PVOID Buffer,
56 IN SIZE_T BufferLength)
57 {
58 PCMHIVE CmHive = (PCMHIVE)RegistryHive;
59 FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
60 if (fseek(File, *FileOffset, SEEK_SET) != 0)
61 return FALSE;
62
63 return (fread(Buffer, 1, BufferLength, File) == BufferLength);
64 }
65
66 static BOOLEAN
67 NTAPI
68 CmpFileWrite(
69 IN PHHIVE RegistryHive,
70 IN ULONG FileType,
71 IN PULONG FileOffset,
72 IN PVOID Buffer,
73 IN SIZE_T BufferLength)
74 {
75 PCMHIVE CmHive = (PCMHIVE)RegistryHive;
76 FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
77 if (fseek(File, *FileOffset, SEEK_SET) != 0)
78 return FALSE;
79
80 return (fwrite(Buffer, 1, BufferLength, File) == BufferLength);
81 }
82
83 static BOOLEAN
84 NTAPI
85 CmpFileSetSize(
86 IN PHHIVE RegistryHive,
87 IN ULONG FileType,
88 IN ULONG FileSize,
89 IN ULONG OldFileSize)
90 {
91 DPRINT1("CmpFileSetSize() unimplemented\n");
92 return FALSE;
93 }
94
95 static BOOLEAN
96 NTAPI
97 CmpFileFlush(
98 IN PHHIVE RegistryHive,
99 IN ULONG FileType,
100 PLARGE_INTEGER FileOffset,
101 ULONG Length)
102 {
103 PCMHIVE CmHive = (PCMHIVE)RegistryHive;
104 FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
105 return (fflush(File) == 0);
106 }
107
108 NTSTATUS
109 CmiInitializeHive(
110 IN OUT PCMHIVE Hive,
111 IN PCWSTR Name)
112 {
113 NTSTATUS Status;
114
115 RtlZeroMemory(Hive, sizeof(*Hive));
116
117 DPRINT("Hive 0x%p\n", Hive);
118
119 Status = HvInitialize(&Hive->Hive,
120 HINIT_CREATE,
121 HIVE_NOLAZYFLUSH,
122 HFILE_TYPE_PRIMARY,
123 0,
124 CmpAllocate,
125 CmpFree,
126 CmpFileSetSize,
127 CmpFileWrite,
128 CmpFileRead,
129 CmpFileFlush,
130 1,
131 NULL);
132 if (!NT_SUCCESS(Status))
133 {
134 return Status;
135 }
136
137 // HACK: See the HACK from r31253
138 if (!CmCreateRootNode(&Hive->Hive, Name))
139 {
140 HvFree(&Hive->Hive);
141 return STATUS_INSUFFICIENT_RESOURCES;
142 }
143
144 /* Add the new hive to the hive list */
145 InsertTailList(&CmiHiveListHead,
146 &Hive->HiveList);
147
148 return STATUS_SUCCESS;
149 }
150
151 NTSTATUS
152 CmiCreateSecurityKey(
153 IN PHHIVE Hive,
154 IN HCELL_INDEX Cell,
155 IN PUCHAR Descriptor,
156 IN ULONG DescriptorLength)
157 {
158 HCELL_INDEX SecurityCell;
159 PCM_KEY_NODE Node;
160 PCM_KEY_SECURITY Security;
161
162 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
163 SecurityCell = HvAllocateCell(Hive,
164 FIELD_OFFSET(CM_KEY_SECURITY, Descriptor) +
165 DescriptorLength,
166 Stable,
167 HCELL_NIL);
168 if (SecurityCell == HCELL_NIL)
169 {
170 HvReleaseCell(Hive, Cell);
171 return STATUS_INSUFFICIENT_RESOURCES;
172 }
173
174 Node->Security = SecurityCell;
175 Security = (PCM_KEY_SECURITY)HvGetCell(Hive, SecurityCell);
176 Security->Signature = CM_KEY_SECURITY_SIGNATURE;
177 Security->ReferenceCount = 1;
178 Security->DescriptorLength = DescriptorLength;
179
180 RtlMoveMemory(&Security->Descriptor,
181 Descriptor,
182 DescriptorLength);
183
184 Security->Flink = Security->Blink = SecurityCell;
185
186 HvReleaseCell(Hive, SecurityCell);
187 HvReleaseCell(Hive, Cell);
188
189 return STATUS_SUCCESS;
190 }
191
192 static NTSTATUS
193 CmiCreateSubKey(
194 IN PCMHIVE RegistryHive,
195 IN HCELL_INDEX ParentKeyCellOffset,
196 IN PCUNICODE_STRING SubKeyName,
197 IN BOOLEAN VolatileKey,
198 OUT HCELL_INDEX* pNKBOffset)
199 {
200 HCELL_INDEX NKBOffset;
201 PCM_KEY_NODE NewKeyCell;
202 UNICODE_STRING KeyName;
203 HSTORAGE_TYPE Storage;
204
205 /* Skip leading path separator if present */
206 if (SubKeyName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
207 {
208 KeyName.Buffer = &SubKeyName->Buffer[1];
209 KeyName.Length = KeyName.MaximumLength = SubKeyName->Length - sizeof(WCHAR);
210 }
211 else
212 {
213 KeyName = *SubKeyName;
214 }
215
216 Storage = (VolatileKey ? Volatile : Stable);
217
218 NKBOffset = HvAllocateCell(&RegistryHive->Hive,
219 FIELD_OFFSET(CM_KEY_NODE, Name) +
220 CmpNameSize(&RegistryHive->Hive, &KeyName),
221 Storage,
222 HCELL_NIL);
223 if (NKBOffset == HCELL_NIL)
224 {
225 return STATUS_INSUFFICIENT_RESOURCES;
226 }
227
228 NewKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, NKBOffset);
229 if (NewKeyCell == NULL)
230 {
231 HvFreeCell(&RegistryHive->Hive, NKBOffset);
232 return STATUS_INSUFFICIENT_RESOURCES;
233 }
234
235 NewKeyCell->Signature = CM_KEY_NODE_SIGNATURE;
236 NewKeyCell->Flags = (VolatileKey ? KEY_IS_VOLATILE : 0);
237 KeQuerySystemTime(&NewKeyCell->LastWriteTime);
238 NewKeyCell->Parent = ParentKeyCellOffset;
239 NewKeyCell->SubKeyCounts[Stable] = 0;
240 NewKeyCell->SubKeyCounts[Volatile] = 0;
241 NewKeyCell->SubKeyLists[Stable] = HCELL_NIL;
242 NewKeyCell->SubKeyLists[Volatile] = HCELL_NIL;
243 NewKeyCell->ValueList.Count = 0;
244 NewKeyCell->ValueList.List = HCELL_NIL;
245 NewKeyCell->Security = HCELL_NIL;
246 NewKeyCell->Class = HCELL_NIL;
247 NewKeyCell->ClassLength = 0;
248 NewKeyCell->MaxNameLen = 0;
249 NewKeyCell->MaxClassLen = 0;
250 NewKeyCell->MaxValueNameLen = 0;
251 NewKeyCell->MaxValueDataLen = 0;
252 NewKeyCell->NameLength = CmpCopyName(&RegistryHive->Hive, NewKeyCell->Name, &KeyName);
253 if (NewKeyCell->NameLength < KeyName.Length) NewKeyCell->Flags |= KEY_COMP_NAME;
254
255 /* Inherit the security from the parent */
256 if (ParentKeyCellOffset == HCELL_NIL)
257 {
258 // We are in fact creating a root key.
259 // This is not handled there, but when we
260 // call CmCreateRootNode instead.
261 ASSERT(FALSE);
262 }
263 else
264 {
265 /* Get the parent node */
266 PCM_KEY_NODE ParentKeyCell;
267 ParentKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, ParentKeyCellOffset);
268
269 if (ParentKeyCell)
270 {
271 /* Inherit the security block of the parent */
272 NewKeyCell->Security = ParentKeyCell->Security;
273 if (NewKeyCell->Security != HCELL_NIL)
274 {
275 PCM_KEY_SECURITY Security;
276 Security = (PCM_KEY_SECURITY)HvGetCell(&RegistryHive->Hive, NewKeyCell->Security);
277 ++Security->ReferenceCount;
278 HvReleaseCell(&RegistryHive->Hive, NewKeyCell->Security);
279 }
280
281 HvReleaseCell(&RegistryHive->Hive, ParentKeyCellOffset);
282 }
283 }
284
285 HvReleaseCell(&RegistryHive->Hive, NKBOffset);
286
287 *pNKBOffset = NKBOffset;
288 return STATUS_SUCCESS;
289 }
290
291 NTSTATUS
292 CmiAddSubKey(
293 IN PCMHIVE RegistryHive,
294 IN HCELL_INDEX ParentKeyCellOffset,
295 IN PCUNICODE_STRING SubKeyName,
296 IN BOOLEAN VolatileKey,
297 OUT HCELL_INDEX *pBlockOffset)
298 {
299 PCM_KEY_NODE ParentKeyCell;
300 HCELL_INDEX NKBOffset;
301 NTSTATUS Status;
302
303 /* Create the new key */
304 Status = CmiCreateSubKey(RegistryHive, ParentKeyCellOffset, SubKeyName, VolatileKey, &NKBOffset);
305 if (!NT_SUCCESS(Status))
306 return Status;
307
308 /* Mark the parent cell as dirty */
309 HvMarkCellDirty(&RegistryHive->Hive, ParentKeyCellOffset, FALSE);
310
311 if (!CmpAddSubKey(&RegistryHive->Hive, ParentKeyCellOffset, NKBOffset))
312 {
313 /* FIXME: delete newly created cell */
314 // CmpFreeKeyByCell(&RegistryHive->Hive, NewCell /*NKBOffset*/, FALSE);
315 ASSERT(FALSE);
316 return STATUS_UNSUCCESSFUL;
317 }
318
319 /* Get the parent node */
320 ParentKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, ParentKeyCellOffset);
321 if (!ParentKeyCell)
322 {
323 /* FIXME: delete newly created cell */
324 return STATUS_UNSUCCESSFUL;
325 }
326 VERIFY_KEY_CELL(ParentKeyCell);
327
328 /* Update the timestamp */
329 KeQuerySystemTime(&ParentKeyCell->LastWriteTime);
330
331 /* Check if we need to update name maximum, update it if so */
332 if (ParentKeyCell->MaxNameLen < SubKeyName->Length)
333 ParentKeyCell->MaxNameLen = SubKeyName->Length;
334
335 /* Release the cell */
336 HvReleaseCell(&RegistryHive->Hive, ParentKeyCellOffset);
337
338 *pBlockOffset = NKBOffset;
339 return STATUS_SUCCESS;
340 }
341
342 NTSTATUS
343 CmiAddValueKey(
344 IN PCMHIVE RegistryHive,
345 IN PCM_KEY_NODE Parent,
346 IN PCUNICODE_STRING ValueName,
347 OUT PCM_KEY_VALUE *pValueCell,
348 OUT HCELL_INDEX *pValueCellOffset)
349 {
350 PCELL_DATA ValueListCell;
351 PCM_KEY_VALUE NewValueCell;
352 HCELL_INDEX ValueListCellOffset;
353 HCELL_INDEX NewValueCellOffset;
354 ULONG CellSize;
355 HSTORAGE_TYPE Storage;
356
357 #ifndef FIELD_SIZE
358 #define FIELD_SIZE(type, field) (sizeof(((type *)0)->field))
359 #endif
360
361 Storage = (Parent->Flags & KEY_IS_VOLATILE) ? Volatile : Stable;
362 if (Parent->ValueList.List == HCELL_NIL)
363 {
364 /* Allocate some room for the value list */
365 CellSize = FIELD_SIZE(CELL_DATA, u.KeyList) + (3 * sizeof(HCELL_INDEX));
366 ValueListCellOffset = HvAllocateCell(&RegistryHive->Hive, CellSize, Storage, HCELL_NIL);
367 if (ValueListCellOffset == HCELL_NIL)
368 return STATUS_INSUFFICIENT_RESOURCES;
369
370 ValueListCell = (PCELL_DATA)HvGetCell(&RegistryHive->Hive, ValueListCellOffset);
371 if (!ValueListCell)
372 {
373 HvFreeCell(&RegistryHive->Hive, ValueListCellOffset);
374 return STATUS_UNSUCCESSFUL;
375 }
376
377 Parent->ValueList.List = ValueListCellOffset;
378 }
379 else
380 {
381 ValueListCell = (PCELL_DATA)HvGetCell(&RegistryHive->Hive, Parent->ValueList.List);
382 if (!ValueListCell)
383 return STATUS_UNSUCCESSFUL;
384
385 CellSize = ABS_VALUE(HvGetCellSize(&RegistryHive->Hive, ValueListCell));
386
387 if (Parent->ValueList.Count >= CellSize / sizeof(HCELL_INDEX))
388 {
389 CellSize *= 2;
390 ValueListCellOffset = HvReallocateCell(&RegistryHive->Hive, Parent->ValueList.List, CellSize);
391 if (ValueListCellOffset == HCELL_NIL)
392 return STATUS_INSUFFICIENT_RESOURCES;
393
394 ValueListCell = (PCELL_DATA)HvGetCell(&RegistryHive->Hive, ValueListCellOffset);
395 if (!ValueListCell)
396 return STATUS_UNSUCCESSFUL;
397
398 Parent->ValueList.List = ValueListCellOffset;
399 }
400 }
401
402
403 NewValueCellOffset = HvAllocateCell(&RegistryHive->Hive,
404 FIELD_OFFSET(CM_KEY_VALUE, Name) +
405 CmpNameSize(&RegistryHive->Hive, (PUNICODE_STRING)ValueName),
406 Storage,
407 HCELL_NIL);
408 if (NewValueCellOffset == HCELL_NIL)
409 {
410 return STATUS_INSUFFICIENT_RESOURCES;
411 }
412
413 NewValueCell = (PCM_KEY_VALUE)HvGetCell(&RegistryHive->Hive, NewValueCellOffset);
414 if (NewValueCell == NULL)
415 {
416 HvFreeCell(&RegistryHive->Hive, NewValueCellOffset);
417 return STATUS_INSUFFICIENT_RESOURCES;
418 }
419
420 NewValueCell->Signature = CM_KEY_VALUE_SIGNATURE;
421 NewValueCell->NameLength = CmpCopyName(&RegistryHive->Hive,
422 NewValueCell->Name,
423 (PUNICODE_STRING)ValueName);
424
425 /* Check for compressed name */
426 if (NewValueCell->NameLength < ValueName->Length)
427 {
428 /* This is a compressed name */
429 NewValueCell->Flags = VALUE_COMP_NAME;
430 }
431 else
432 {
433 /* No flags to set */
434 NewValueCell->Flags = 0;
435 }
436
437 NewValueCell->Type = 0;
438 NewValueCell->DataLength = 0;
439 NewValueCell->Data = HCELL_NIL;
440
441
442 ValueListCell->u.KeyList[Parent->ValueList.Count] = NewValueCellOffset;
443 Parent->ValueList.Count++;
444
445 HvMarkCellDirty(&RegistryHive->Hive, Parent->ValueList.List, FALSE);
446 HvMarkCellDirty(&RegistryHive->Hive, NewValueCellOffset, FALSE);
447
448 *pValueCell = NewValueCell;
449 *pValueCellOffset = NewValueCellOffset;
450
451 return STATUS_SUCCESS;
452 }