9491affe6fc8d5b9a133322ba4402864c270c847
[reactos.git] / reactos / tools / mkhive / binhive.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2003, 2004 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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS hive maker
22 * FILE: tools/mkhive/binhive.c
23 * PURPOSE: Binary hive export code
24 * PROGRAMMER: Eric Kohl
25 */
26
27 /* INCLUDES *****************************************************************/
28
29 //#include <assert.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "mkhive.h"
35 #include "binhive.h"
36 #include "registry.h"
37
38
39 #define REG_HIVE_ID 0x66676572
40 #define REG_BIN_ID 0x6e696268
41 #define REG_KEY_CELL_ID 0x6b6e
42 #define REG_HASH_TABLE_CELL_ID 0x666c
43 #define REG_VALUE_CELL_ID 0x6b76
44
45 #define REG_BLOCK_SIZE 4096
46 #define REG_HBIN_DATA_OFFSET 32
47 #define REG_INIT_BLOCK_LIST_SIZE 32
48 #define REG_INIT_HASH_TABLE_SIZE 3
49 #define REG_EXTEND_HASH_TABLE_SIZE 4
50 #define REG_VALUE_LIST_CELL_MULTIPLE 4
51
52 #define ROUND_UP_POW2(N,S) (((N) + (S) - 1) & ~((S) - 1))
53 #define ROUND_DOWN_POW2(N,S) ((N) & ~((S) - 1))
54
55 #define ABS_VALUE(V) (((V) < 0) ? -(V) : (V))
56
57
58 // BLOCK_OFFSET = offset in file after header block
59 typedef ULONG BLOCK_OFFSET, *PBLOCK_OFFSET;
60
61 #ifdef _MSC_VER
62 typedef unsigned __int64 FILETIME;
63 #else
64 typedef unsigned long long FILETIME;
65 #endif
66
67 #ifdef _MSC_VER
68 #pragma pack ( push, hive_header, 1 )
69 #endif//_MSC_VER
70
71 /* header for registry hive file : */
72 typedef struct _HIVE_HEADER
73 {
74 /* Hive identifier "regf" (0x66676572) */
75 ULONG BlockId;
76
77 /* Update counter */
78 ULONG UpdateCounter1;
79
80 /* Update counter */
81 ULONG UpdateCounter2;
82
83 /* When this hive file was last modified */
84 FILETIME DateModified;
85
86 /* Registry format major version (1) */
87 ULONG MajorVersion;
88
89 /* Registry format minor version (3)
90 Version 3 added fast indexes, version 5 has large value optimizations */
91 ULONG MinorVersion;
92
93 /* Registry file type (0 - Primary, 1 - Log) */
94 ULONG Type;
95
96 /* Registry format (1 is the only defined value so far) */
97 ULONG Format;
98
99 /* Offset into file from the byte after the end of the base block.
100 If the hive is volatile, this is the actual pointer to the KEY_CELL */
101 BLOCK_OFFSET RootKeyOffset;
102
103 /* Size of each hive block ? */
104 ULONG BlockSize;
105
106 /* (1?) */
107 ULONG Unused7;
108
109 /* Name of hive file */
110 WCHAR FileName[48];
111
112 ULONG Reserved[99];
113
114 /* Checksum of first 0x200 bytes */
115 ULONG Checksum;
116 } GCC_PACKED HIVE_HEADER, *PHIVE_HEADER;
117
118 typedef struct _HBIN
119 {
120 /* Bin identifier "hbin" (0x6E696268) */
121 ULONG HeaderId;
122
123 /* Block offset of this bin */
124 BLOCK_OFFSET BinOffset;
125
126 /* Size in bytes, multiple of the block size (4KB) */
127 ULONG BinSize;
128
129 ULONG Reserved[2];
130
131 /* When this bin was last modified */
132 FILETIME DateModified;
133
134 /* ? (In-memory only) */
135 ULONG MemAlloc;
136 } GCC_PACKED HBIN, *PHBIN;
137
138 typedef struct _CELL_HEADER
139 {
140 /* <0 if used, >0 if free */
141 LONG CellSize;
142 } GCC_PACKED CELL_HEADER, *PCELL_HEADER;
143
144 typedef struct _KEY_CELL
145 {
146 /* Size of this cell */
147 LONG CellSize;
148
149 /* Key cell identifier "kn" (0x6b6e) */
150 USHORT Id;
151
152 /* ? */
153 USHORT Type;
154
155 /* Time of last flush */
156 FILETIME LastWriteTime;
157
158 /* ? */
159 ULONG UnUsed1;
160
161 /* Block offset of parent key cell */
162 BLOCK_OFFSET ParentKeyOffset;
163
164 /* Count of sub keys for the key in this key cell */
165 ULONG NumberOfSubKeys;
166
167 /* ? */
168 ULONG UnUsed2;
169
170 /* Block offset of has table for FIXME: subkeys/values? */
171 BLOCK_OFFSET HashTableOffset;
172
173 /* ? */
174 ULONG UnUsed3;
175
176 /* Count of values contained in this key cell */
177 ULONG NumberOfValues;
178
179 /* Block offset of VALUE_LIST_CELL */
180 BLOCK_OFFSET ValueListOffset;
181
182 /* Block offset of security cell */
183 BLOCK_OFFSET SecurityKeyOffset;
184
185 /* Block offset of registry key class */
186 BLOCK_OFFSET ClassNameOffset;
187
188 /* ? */
189 ULONG Unused4[5];
190
191 /* Size in bytes of key name */
192 USHORT NameSize;
193
194 /* Size of class name in bytes */
195 USHORT ClassSize;
196
197 /* Name of key (not zero terminated) */
198 CHAR Name[0];
199 } GCC_PACKED KEY_CELL, *PKEY_CELL;
200
201 /* KEY_CELL.Type constants */
202 #define REG_LINK_KEY_CELL_TYPE 0x10
203 #define REG_KEY_CELL_TYPE 0x20
204 #define REG_ROOT_KEY_CELL_TYPE 0x2c
205
206
207 // hash record :
208 // HashValue=four letters of value's name
209 typedef struct _HASH_RECORD
210 {
211 BLOCK_OFFSET KeyOffset;
212 ULONG HashValue;
213 } GCC_PACKED HASH_RECORD, *PHASH_RECORD;
214
215 typedef struct _HASH_TABLE_CELL
216 {
217 LONG CellSize;
218 USHORT Id;
219 USHORT HashTableSize;
220 HASH_RECORD Table[0];
221 } GCC_PACKED HASH_TABLE_CELL, *PHASH_TABLE_CELL;
222
223 typedef struct _VALUE_LIST_CELL
224 {
225 LONG CellSize;
226 BLOCK_OFFSET ValueOffset[0];
227 } GCC_PACKED VALUE_LIST_CELL, *PVALUE_LIST_CELL;
228
229 typedef struct _VALUE_CELL
230 {
231 LONG CellSize;
232 USHORT Id; // "kv"
233 USHORT NameSize; // length of Name
234 ULONG DataSize; // length of datas in the cell pointed by DataOffset
235 BLOCK_OFFSET DataOffset;// datas are here if high bit of DataSize is set
236 ULONG DataType;
237 USHORT Flags;
238 USHORT Unused1;
239 CHAR Name[0]; /* warning : not zero terminated */
240 } GCC_PACKED VALUE_CELL, *PVALUE_CELL;
241
242 /* VALUE_CELL.Flags constants */
243 #define REG_VALUE_NAME_PACKED 0x0001
244
245 /* VALUE_CELL.DataSize mask constants */
246 #define REG_DATA_SIZE_MASK 0x7FFFFFFF
247 #define REG_DATA_IN_OFFSET 0x80000000
248
249 typedef struct _DATA_CELL
250 {
251 LONG CellSize;
252 CHAR Data[0];
253 } GCC_PACKED DATA_CELL, *PDATA_CELL;
254
255 #ifdef _MSC_VER
256 #pragma pack ( pop, hive_header )
257 #endif//_MSC_VER
258
259 typedef struct _REGISTRY_HIVE
260 {
261 ULONG FileSize;
262 PHIVE_HEADER HiveHeader;
263 ULONG BlockListSize;
264 PHBIN *BlockList;
265 ULONG FreeListSize;
266 ULONG FreeListMax;
267 PCELL_HEADER *FreeList;
268 BLOCK_OFFSET *FreeListOffset;
269 } REGISTRY_HIVE, *PREGISTRY_HIVE;
270
271 /* FUNCTIONS ****************************************************************/
272
273 static VOID
274 memexpand (PWCHAR Dst,
275 PCHAR Src,
276 ULONG Length)
277 {
278 ULONG i;
279
280 for (i = 0; i < Length; i++)
281 Dst[i] = (WCHAR)Src[i];
282 }
283
284
285 static VOID
286 CmiCreateDefaultHiveHeader (PHIVE_HEADER Header)
287 {
288 assert (Header);
289 memset (Header, 0, REG_BLOCK_SIZE);
290 Header->BlockId = REG_HIVE_ID;
291 Header->UpdateCounter1 = 0;
292 Header->UpdateCounter2 = 0;
293 Header->DateModified = 0;
294 Header->MajorVersion = 1;
295 Header->MinorVersion = 3;
296 Header->Type = 0;
297 Header->Format = 1;
298 Header->Unused7 = 1;
299 Header->RootKeyOffset = -1;
300 Header->BlockSize = REG_BLOCK_SIZE;
301 Header->Checksum = 0;
302 }
303
304
305 static VOID
306 CmiCreateDefaultBinCell(PHBIN BinCell)
307 {
308 assert (BinCell);
309 memset (BinCell, 0, REG_BLOCK_SIZE);
310 BinCell->HeaderId = REG_BIN_ID;
311 BinCell->DateModified = 0;
312 BinCell->BinSize = REG_BLOCK_SIZE;
313 }
314
315
316 static VOID
317 CmiCreateDefaultRootKeyCell(PKEY_CELL RootKeyCell, PCHAR KeyName)
318 {
319 PCHAR BaseKeyName;
320 USHORT NameSize;
321 ULONG CellSize;
322
323 assert (RootKeyCell);
324
325 BaseKeyName = strrchr(KeyName, '\\') + 1;
326 NameSize = strlen(BaseKeyName);
327 CellSize = ROUND_UP_POW2(sizeof(KEY_CELL) + NameSize - 1, 16);
328
329 memset (RootKeyCell, 0, CellSize);
330 RootKeyCell->CellSize = (ULONG)-(LONG)CellSize;
331 RootKeyCell->Id = REG_KEY_CELL_ID;
332 RootKeyCell->Type = REG_ROOT_KEY_CELL_TYPE;
333 RootKeyCell->LastWriteTime = 0;
334 RootKeyCell->ParentKeyOffset = 0;
335 RootKeyCell->NumberOfSubKeys = 0;
336 RootKeyCell->HashTableOffset = -1;
337 RootKeyCell->NumberOfValues = 0;
338 RootKeyCell->ValueListOffset = -1;
339 RootKeyCell->SecurityKeyOffset = 0;
340 RootKeyCell->ClassNameOffset = -1;
341 RootKeyCell->NameSize = NameSize;
342 RootKeyCell->ClassSize = 0;
343 memcpy (RootKeyCell->Name,
344 BaseKeyName,
345 NameSize);
346 }
347
348
349 static PREGISTRY_HIVE
350 CmiCreateRegistryHive (PCHAR KeyName)
351 {
352 PREGISTRY_HIVE Hive;
353 PCELL_HEADER FreeCell;
354 PKEY_CELL RootKeyCell;
355 PHBIN BinCell;
356
357 Hive = (PREGISTRY_HIVE) malloc (sizeof(REGISTRY_HIVE));
358 if (Hive == NULL)
359 {
360 return NULL;
361 }
362 memset (Hive, 0, sizeof(REGISTRY_HIVE));
363
364 DPRINT("Hive %p\n", Hive);
365
366 /* Create hive beader (aka 'base block') */
367 Hive->HiveHeader = (PHIVE_HEADER) malloc (REG_BLOCK_SIZE);
368 if (Hive->HiveHeader == NULL)
369 {
370 free (Hive);
371 return NULL;
372 }
373 CmiCreateDefaultHiveHeader (Hive->HiveHeader);
374 Hive->FileSize = REG_BLOCK_SIZE;
375
376 /* Allocate block list */
377 Hive->BlockListSize = 1;
378 Hive->BlockList = malloc (sizeof(PHBIN) * Hive->BlockListSize);
379 if (Hive->BlockList == NULL)
380 {
381 free (Hive->HiveHeader);
382 free (Hive);
383 return NULL;
384 }
385
386 /* Allocate free cell list */
387 Hive->FreeListMax = 32;
388 Hive->FreeList = malloc(sizeof(PCELL_HEADER) * Hive->FreeListMax);
389 if (Hive->FreeList == NULL)
390 {
391 free (Hive->BlockList);
392 free (Hive->HiveHeader);
393 free (Hive);
394 return NULL;
395 }
396 Hive->FreeListOffset = malloc(sizeof(BLOCK_OFFSET) * Hive->FreeListMax);
397 if (Hive->FreeListOffset == NULL)
398 {
399 free (Hive->FreeList);
400 free (Hive->BlockList);
401 free (Hive->HiveHeader);
402 free (Hive);
403 return NULL;
404 }
405
406 /* Allocate first bin */
407 Hive->BlockList[0] = (PHBIN) malloc (REG_BLOCK_SIZE);
408 if (Hive->BlockList[0] == NULL)
409 {
410 free (Hive->FreeListOffset);
411 free (Hive->FreeList);
412 free (Hive->BlockList);
413 free (Hive->HiveHeader);
414 free (Hive);
415 return NULL;
416 }
417 Hive->FileSize += REG_BLOCK_SIZE;
418
419 /* Init first bin */
420 BinCell = (PHBIN)Hive->BlockList[0];
421 CmiCreateDefaultBinCell (BinCell);
422 BinCell->BinOffset = 0;
423
424 /* Init root key cell */
425 RootKeyCell = (PKEY_CELL)((ULONG_PTR)BinCell + REG_HBIN_DATA_OFFSET);
426 CmiCreateDefaultRootKeyCell (RootKeyCell, KeyName);
427 Hive->HiveHeader->RootKeyOffset = REG_HBIN_DATA_OFFSET;
428
429 /* Init free cell */
430 FreeCell = (PCELL_HEADER)((ULONG_PTR)RootKeyCell - RootKeyCell->CellSize);
431 FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET - RootKeyCell->CellSize);
432
433 Hive->FreeList[0] = FreeCell;
434 Hive->FreeListOffset[0] = REG_HBIN_DATA_OFFSET - RootKeyCell->CellSize;
435 Hive->FreeListSize++;
436
437 return Hive;
438 }
439
440
441 static VOID
442 CmiDestroyRegistryHive (PREGISTRY_HIVE Hive)
443 {
444 PHBIN Bin;
445 ULONG i;
446
447 if (Hive == NULL)
448 return;
449
450 /* Release free offset list */
451 if (Hive->FreeListOffset != NULL)
452 free (Hive->FreeListOffset);
453
454 /* Release free list */
455 if (Hive->FreeList != NULL)
456 free (Hive->FreeList);
457
458 if (Hive->BlockList != NULL)
459 {
460 /* Release bins */
461 Bin = NULL;
462 for (i = 0; i < Hive->BlockListSize; i++)
463 {
464 if ((Hive->BlockList[i] != NULL) &&
465 (Hive->BlockList[i] != Bin))
466 {
467 Bin = Hive->BlockList[i];
468
469 DPRINT ("Bin[%lu]: Offset 0x%lx Size 0x%lx\n",
470 i, Bin->BinOffset, Bin->BinSize);
471
472 free (Bin);
473 }
474 }
475
476 /* Release block list */
477 free (Hive->BlockList);
478 }
479
480 /* Release hive header */
481 if (Hive->HiveHeader != NULL)
482 free (Hive->HiveHeader);
483
484 /* Release hive */
485 free (Hive);
486 }
487
488
489 static PVOID
490 CmiGetCell (PREGISTRY_HIVE Hive,
491 BLOCK_OFFSET BlockOffset,
492 PHBIN * ppBin)
493 {
494 PHBIN pBin;
495 ULONG BlockIndex;
496
497 if (ppBin)
498 *ppBin = NULL;
499
500 if (BlockOffset == (ULONG_PTR) -1)
501 return NULL;
502
503 BlockIndex = BlockOffset / 4096;
504 if (BlockIndex >= Hive->BlockListSize)
505 return NULL;
506
507 pBin = Hive->BlockList[BlockIndex];
508 if (ppBin)
509 *ppBin = pBin;
510
511 return (PVOID)((ULONG_PTR)pBin + (BlockOffset - pBin->BinOffset));
512 }
513
514
515 static BOOL
516 CmiMergeFree(PREGISTRY_HIVE RegistryHive,
517 PCELL_HEADER FreeBlock,
518 BLOCK_OFFSET FreeOffset)
519 {
520 BLOCK_OFFSET BlockOffset;
521 BLOCK_OFFSET BinOffset;
522 ULONG BlockSize;
523 ULONG BinSize;
524 PHBIN Bin;
525 ULONG i;
526
527 DPRINT("CmiMergeFree(Block %p Offset %lx Size %lx) called\n",
528 FreeBlock, FreeOffset, FreeBlock->CellSize);
529
530 CmiGetCell (RegistryHive,
531 FreeOffset,
532 &Bin);
533 DPRINT("Bin %p\n", Bin);
534 if (Bin == NULL)
535 return FALSE;
536
537 BinOffset = Bin->BinOffset;
538 BinSize = Bin->BinSize;
539 DPRINT("Bin %p Offset %lx Size %lx\n", Bin, BinOffset, BinSize);
540
541 for (i = 0; i < RegistryHive->FreeListSize; i++)
542 {
543 BlockOffset = RegistryHive->FreeListOffset[i];
544 BlockSize = RegistryHive->FreeList[i]->CellSize;
545 if (BlockOffset > BinOffset &&
546 BlockOffset < BinOffset + BinSize)
547 {
548 DPRINT("Free block: Offset %lx Size %lx\n",
549 BlockOffset, BlockSize);
550
551 if ((i < (RegistryHive->FreeListSize - 1)) &&
552 (BlockOffset + BlockSize == FreeOffset) &&
553 (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
554 {
555 DPRINT("Merge current block with previous and next block\n");
556
557 RegistryHive->FreeList[i]->CellSize +=
558 (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
559
560 FreeBlock->CellSize = 0;
561 RegistryHive->FreeList[i + 1]->CellSize = 0;
562
563
564 if ((i + 2) < RegistryHive->FreeListSize)
565 {
566 memmove (&RegistryHive->FreeList[i + 1],
567 &RegistryHive->FreeList[i + 2],
568 sizeof(RegistryHive->FreeList[0])
569 * (RegistryHive->FreeListSize - i - 2));
570 memmove (&RegistryHive->FreeListOffset[i + 1],
571 &RegistryHive->FreeListOffset[i + 2],
572 sizeof(RegistryHive->FreeListOffset[0])
573 * (RegistryHive->FreeListSize - i - 2));
574 }
575 RegistryHive->FreeListSize--;
576
577 return TRUE;
578 }
579 else if (BlockOffset + BlockSize == FreeOffset)
580 {
581 DPRINT("Merge current block with previous block\n");
582
583 RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
584 FreeBlock->CellSize = 0;
585
586 return TRUE;
587 }
588 else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
589 {
590 DPRINT("Merge current block with next block\n");
591
592 FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
593 RegistryHive->FreeList[i]->CellSize = 0;
594 RegistryHive->FreeList[i] = FreeBlock;
595 RegistryHive->FreeListOffset[i] = FreeOffset;
596
597 return TRUE;
598 }
599 }
600 }
601
602 return FALSE;
603 }
604
605
606 static BOOL
607 CmiAddFree(PREGISTRY_HIVE RegistryHive,
608 PCELL_HEADER FreeBlock,
609 BLOCK_OFFSET FreeOffset,
610 BOOL MergeFreeBlocks)
611 {
612 PCELL_HEADER *tmpList;
613 BLOCK_OFFSET *tmpListOffset;
614 LONG minInd;
615 LONG maxInd;
616 LONG medInd;
617
618 assert(RegistryHive);
619 assert(FreeBlock);
620
621 DPRINT("FreeBlock %p FreeOffset %.08lx\n",
622 FreeBlock, FreeOffset);
623
624 /* Merge free blocks */
625 if (MergeFreeBlocks == TRUE)
626 {
627 if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
628 return TRUE;
629 }
630
631 if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
632 {
633 tmpList = malloc (sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
634 if (tmpList == NULL)
635 {
636 return FALSE;
637 }
638
639 tmpListOffset = malloc (sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
640 if (tmpListOffset == NULL)
641 {
642 free (tmpList);
643 return FALSE;
644 }
645
646 if (RegistryHive->FreeListMax)
647 {
648 memmove (tmpList,
649 RegistryHive->FreeList,
650 sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
651 memmove (tmpListOffset,
652 RegistryHive->FreeListOffset,
653 sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax));
654 free (RegistryHive->FreeList);
655 free (RegistryHive->FreeListOffset);
656 }
657 RegistryHive->FreeList = tmpList;
658 RegistryHive->FreeListOffset = tmpListOffset;
659 RegistryHive->FreeListMax += 32;
660 }
661
662 /* Add new offset to free list, maintaining list in ascending order */
663 if ((RegistryHive->FreeListSize == 0)
664 || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
665 {
666 /* Add to end of list */
667 RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
668 RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
669 }
670 else if (RegistryHive->FreeListOffset[0] > FreeOffset)
671 {
672 /* Add to begin of list */
673 memmove (&RegistryHive->FreeList[1],
674 &RegistryHive->FreeList[0],
675 sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
676 memmove (&RegistryHive->FreeListOffset[1],
677 &RegistryHive->FreeListOffset[0],
678 sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
679 RegistryHive->FreeList[0] = FreeBlock;
680 RegistryHive->FreeListOffset[0] = FreeOffset;
681 RegistryHive->FreeListSize++;
682 }
683 else
684 {
685 /* Search where to insert */
686 minInd = 0;
687 maxInd = RegistryHive->FreeListSize - 1;
688 while ((maxInd - minInd) > 1)
689 {
690 medInd = (minInd + maxInd) / 2;
691 if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
692 maxInd = medInd;
693 else
694 minInd = medInd;
695 }
696
697 /* Insert before maxInd */
698 memmove (&RegistryHive->FreeList[maxInd+1],
699 &RegistryHive->FreeList[maxInd],
700 sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
701 memmove (&RegistryHive->FreeListOffset[maxInd + 1],
702 &RegistryHive->FreeListOffset[maxInd],
703 sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
704 RegistryHive->FreeList[maxInd] = FreeBlock;
705 RegistryHive->FreeListOffset[maxInd] = FreeOffset;
706 RegistryHive->FreeListSize++;
707 }
708
709 return TRUE;
710 }
711
712
713 static BOOL
714 CmiAddBin(PREGISTRY_HIVE RegistryHive,
715 ULONG BlockCount,
716 PVOID *NewBlock,
717 PBLOCK_OFFSET NewBlockOffset)
718 {
719 PCELL_HEADER tmpBlock;
720 PHBIN * tmpBlockList;
721 PHBIN tmpBin;
722 ULONG BinSize;
723 ULONG i;
724
725 BinSize = BlockCount *REG_BLOCK_SIZE;
726 tmpBin = malloc (BinSize);
727 if (tmpBin == NULL)
728 {
729 return FALSE;
730 }
731 memset (tmpBin, 0, BinSize);
732
733 tmpBin->HeaderId = REG_BIN_ID;
734 tmpBin->BinOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
735 RegistryHive->FileSize += BinSize;
736 tmpBin->BinSize = BinSize;
737 tmpBin->DateModified = 0;
738 tmpBin->MemAlloc = 0;
739
740 /* Increase size of list of blocks */
741 tmpBlockList = malloc (sizeof(PHBIN) * (RegistryHive->BlockListSize + BlockCount));
742 if (tmpBlockList == NULL)
743 {
744 free (tmpBin);
745 return FALSE;
746 }
747
748 if (RegistryHive->BlockListSize > 0)
749 {
750 memcpy (tmpBlockList,
751 RegistryHive->BlockList,
752 sizeof(PHBIN) * RegistryHive->BlockListSize);
753 free (RegistryHive->BlockList);
754 }
755
756 RegistryHive->BlockList = tmpBlockList;
757 for (i = 0; i < BlockCount; i++)
758 RegistryHive->BlockList[RegistryHive->BlockListSize + i] = tmpBin;
759 RegistryHive->BlockListSize += BlockCount;
760
761 /* Initialize a free block in this heap : */
762 tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
763 tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
764
765 *NewBlock = (PVOID) tmpBlock;
766
767 if (NewBlockOffset)
768 *NewBlockOffset = tmpBin->BinOffset + REG_HBIN_DATA_OFFSET;
769
770 return TRUE;
771 }
772
773
774 static BOOL
775 CmiAllocateCell (PREGISTRY_HIVE RegistryHive,
776 LONG CellSize,
777 PVOID *Block,
778 PBLOCK_OFFSET pBlockOffset)
779 {
780 PCELL_HEADER NewBlock;
781 ULONG i;
782
783 *Block = NULL;
784
785 /* Round to 16 bytes multiple */
786 CellSize = ROUND_UP_POW2(CellSize, 16);
787
788 /* first search in free blocks */
789 NewBlock = NULL;
790 for (i = 0; i < RegistryHive->FreeListSize; i++)
791 {
792 if (RegistryHive->FreeList[i]->CellSize >= CellSize)
793 {
794 NewBlock = RegistryHive->FreeList[i];
795 if (pBlockOffset)
796 *pBlockOffset = RegistryHive->FreeListOffset[i];
797
798 if ((i + 1) < RegistryHive->FreeListSize)
799 {
800 memmove (&RegistryHive->FreeList[i],
801 &RegistryHive->FreeList[i + 1],
802 sizeof(RegistryHive->FreeList[0])
803 * (RegistryHive->FreeListSize - i - 1));
804 memmove (&RegistryHive->FreeListOffset[i],
805 &RegistryHive->FreeListOffset[i + 1],
806 sizeof(RegistryHive->FreeListOffset[0])
807 * (RegistryHive->FreeListSize - i - 1));
808 }
809 RegistryHive->FreeListSize--;
810 break;
811 }
812 }
813
814 /* Need to extend hive file : */
815 if (NewBlock == NULL)
816 {
817 /* Add a new block */
818 if (!CmiAddBin(RegistryHive,
819 ((sizeof(HBIN) + CellSize - 1) / REG_BLOCK_SIZE) + 1,
820 (PVOID *)&NewBlock,
821 pBlockOffset))
822 return FALSE;
823 }
824
825 *Block = NewBlock;
826
827 /* Split the block in two parts */
828 if (NewBlock->CellSize > CellSize)
829 {
830 NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock + CellSize);
831 NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - CellSize;
832 ((PCELL_HEADER) (*Block))->CellSize = CellSize;
833 CmiAddFree (RegistryHive,
834 NewBlock,
835 *pBlockOffset + CellSize,
836 TRUE);
837 }
838 else if (NewBlock->CellSize < CellSize)
839 {
840 return FALSE;
841 }
842
843 memset((PVOID)((ULONG_PTR)*Block + sizeof(CELL_HEADER)), 0,
844 CellSize - sizeof(CELL_HEADER));
845 ((PCELL_HEADER)(*Block))->CellSize *= -1;
846
847 return TRUE;
848 }
849
850
851 static BOOL
852 CmiAllocateHashTableCell (PREGISTRY_HIVE Hive,
853 PBLOCK_OFFSET HBOffset,
854 USHORT SubKeyCount)
855 {
856 PHASH_TABLE_CELL HashCell;
857 ULONG NewHashSize;
858 BOOL Status;
859
860 NewHashSize = sizeof(HASH_TABLE_CELL) +
861 (SubKeyCount * sizeof(HASH_RECORD));
862 Status = CmiAllocateCell (Hive,
863 NewHashSize,
864 (PVOID*) &HashCell,
865 HBOffset);
866 if ((HashCell == NULL) || (Status == FALSE))
867 {
868 return FALSE;
869 }
870
871 HashCell->Id = REG_HASH_TABLE_CELL_ID;
872 HashCell->HashTableSize = SubKeyCount;
873
874 return TRUE;
875 }
876
877
878 static BOOL
879 CmiAddKeyToParentHashTable (PREGISTRY_HIVE Hive,
880 BLOCK_OFFSET ParentKeyOffset,
881 PKEY_CELL NewKeyCell,
882 BLOCK_OFFSET NKBOffset)
883 {
884 PHASH_TABLE_CELL HashBlock;
885 PKEY_CELL ParentKeyCell;
886 ULONG i;
887
888 ParentKeyCell = CmiGetCell (Hive,
889 ParentKeyOffset,
890 NULL);
891 if (ParentKeyCell == NULL)
892 {
893 DPRINT1 ("CmiGetBlock() failed\n");
894 return FALSE;
895 }
896
897 HashBlock =CmiGetCell (Hive,
898 ParentKeyCell->HashTableOffset,
899 NULL);
900 if (HashBlock == NULL)
901 {
902 DPRINT1 ("CmiGetBlock() failed\n");
903 return FALSE;
904 }
905
906 for (i = 0; i < HashBlock->HashTableSize; i++)
907 {
908 if (HashBlock->Table[i].KeyOffset == 0)
909 {
910 HashBlock->Table[i].KeyOffset = NKBOffset;
911 memcpy (&HashBlock->Table[i].HashValue,
912 NewKeyCell->Name,
913 4);
914 ParentKeyCell->NumberOfSubKeys++;
915 return TRUE;
916 }
917 }
918
919 return FALSE;
920 }
921
922
923 static BOOL
924 CmiAllocateValueListCell (PREGISTRY_HIVE Hive,
925 PBLOCK_OFFSET ValueListOffset,
926 ULONG ValueCount)
927 {
928 PVALUE_LIST_CELL ValueListCell;
929 ULONG ValueListSize;
930 BOOL Status;
931
932 ValueListSize = sizeof(VALUE_LIST_CELL) +
933 (ValueCount * sizeof(BLOCK_OFFSET));
934 Status = CmiAllocateCell (Hive,
935 ValueListSize,
936 (PVOID)&ValueListCell,
937 ValueListOffset);
938 if ((ValueListCell == NULL) || (Status == FALSE))
939 {
940 DPRINT1 ("CmiAllocateBlock() failed\n");
941 return FALSE;
942 }
943
944 return TRUE;
945 }
946
947
948 static BOOL
949 CmiAllocateValueCell(PREGISTRY_HIVE Hive,
950 PVALUE_CELL *ValueCell,
951 BLOCK_OFFSET *ValueCellOffset,
952 PCHAR ValueName)
953 {
954 PVALUE_CELL NewValueCell;
955 USHORT NameSize;
956 BOOL Status;
957
958 NameSize = (ValueName == NULL) ? 0 : strlen (ValueName);
959 Status = CmiAllocateCell (Hive,
960 sizeof(VALUE_CELL) + NameSize,
961 (PVOID*)&NewValueCell,
962 ValueCellOffset);
963 if ((NewValueCell == NULL) || (Status == FALSE))
964 {
965 DPRINT1 ("CmiAllocateBlock() failed\n");
966 return FALSE;
967 }
968
969 NewValueCell->Id = REG_VALUE_CELL_ID;
970 NewValueCell->NameSize = NameSize;
971 if (NameSize > 0)
972 {
973 memcpy (NewValueCell->Name,
974 ValueName,
975 NameSize);
976 NewValueCell->Flags = REG_VALUE_NAME_PACKED;
977 }
978 NewValueCell->DataType = 0;
979 NewValueCell->DataSize = 0;
980 NewValueCell->DataOffset = -1;
981
982 *ValueCell = NewValueCell;
983
984 return TRUE;
985 }
986
987
988 static BOOL
989 CmiAddValueToKeyValueList(PREGISTRY_HIVE Hive,
990 BLOCK_OFFSET KeyCellOffset,
991 BLOCK_OFFSET ValueCellOffset)
992 {
993 PVALUE_LIST_CELL ValueListCell;
994 PKEY_CELL KeyCell;
995
996 KeyCell = CmiGetCell (Hive, KeyCellOffset, NULL);
997 if (KeyCell == NULL)
998 {
999 DPRINT1 ("CmiGetBlock() failed\n");
1000 return FALSE;
1001 }
1002
1003 ValueListCell = CmiGetCell (Hive, KeyCell->ValueListOffset, NULL);
1004 if (ValueListCell == NULL)
1005 {
1006 DPRINT1 ("CmiGetBlock() failed\n");
1007 return FALSE;
1008 }
1009
1010 ValueListCell->ValueOffset[KeyCell->NumberOfValues] = ValueCellOffset;
1011 KeyCell->NumberOfValues++;
1012
1013 return TRUE;
1014 }
1015
1016
1017 static BOOL
1018 CmiExportValue (PREGISTRY_HIVE Hive,
1019 BLOCK_OFFSET KeyCellOffset,
1020 HKEY Key,
1021 PVALUE Value)
1022 {
1023 BLOCK_OFFSET ValueCellOffset;
1024 BLOCK_OFFSET DataCellOffset;
1025 PVALUE_CELL ValueCell;
1026 PDATA_CELL DataCell;
1027 ULONG SrcDataSize;
1028 ULONG DstDataSize;
1029 ULONG DataType;
1030 PCHAR Data;
1031 BOOL Expand = FALSE;
1032
1033 DPRINT ("CmiExportValue('%s') called\n", (Value == NULL) ? "<default>" : (PCHAR)Value->Name);
1034 DPRINT ("DataSize %lu\n", (Value == NULL) ? Key->DataSize : Value->DataSize);
1035
1036 /* Allocate value cell */
1037 if (!CmiAllocateValueCell(Hive, &ValueCell, &ValueCellOffset, (Value == NULL) ? NULL : Value->Name))
1038 {
1039 return FALSE;
1040 }
1041
1042 if (!CmiAddValueToKeyValueList(Hive, KeyCellOffset, ValueCellOffset))
1043 {
1044 return FALSE;
1045 }
1046
1047 if (Value == NULL)
1048 {
1049 DataType = Key->DataType;
1050 SrcDataSize = Key->DataSize;
1051 Data = Key->Data;
1052 }
1053 else
1054 {
1055 DataType = Value->DataType;
1056 SrcDataSize = Value->DataSize;
1057 Data = Value->Data;
1058 }
1059
1060 DstDataSize = SrcDataSize;
1061 if (DataType == REG_SZ ||
1062 DataType == REG_EXPAND_SZ ||
1063 DataType == REG_MULTI_SZ)
1064 {
1065 DstDataSize *= sizeof(WCHAR);
1066 Expand = TRUE;
1067 }
1068
1069 if ((DstDataSize & REG_DATA_SIZE_MASK) <= sizeof(BLOCK_OFFSET))
1070 {
1071 ValueCell->DataSize = DstDataSize | REG_DATA_IN_OFFSET;
1072 ValueCell->DataType = DataType;
1073 if (Expand)
1074 {
1075 memexpand ((PWCHAR)&ValueCell->DataOffset,
1076 (PCHAR)&Data,
1077 SrcDataSize);
1078 }
1079 else
1080 {
1081 memcpy (&ValueCell->DataOffset,
1082 &Data,
1083 SrcDataSize);
1084 }
1085 }
1086 else
1087 {
1088 /* Allocate data cell */
1089 if (!CmiAllocateCell (Hive,
1090 sizeof(CELL_HEADER) + DstDataSize,
1091 (PVOID *)&DataCell,
1092 &DataCellOffset))
1093 {
1094 return FALSE;
1095 }
1096
1097 ValueCell->DataOffset = DataCellOffset;
1098 ValueCell->DataSize = DstDataSize & REG_DATA_SIZE_MASK;
1099 ValueCell->DataType = DataType;
1100
1101 if (Expand)
1102 {
1103 if (SrcDataSize <= sizeof(BLOCK_OFFSET))
1104 {
1105 memexpand ((PWCHAR)DataCell->Data,
1106 (PCHAR)&Data,
1107 SrcDataSize);
1108 }
1109 else
1110 {
1111 memexpand ((PWCHAR)DataCell->Data,
1112 Data,
1113 SrcDataSize);
1114 }
1115 }
1116 else
1117 {
1118 memcpy (DataCell->Data,
1119 Data,
1120 SrcDataSize);
1121 }
1122 }
1123
1124 return TRUE;
1125 }
1126
1127
1128 static BOOL
1129 CmiExportSubKey (PREGISTRY_HIVE Hive,
1130 BLOCK_OFFSET ParentKeyOffset,
1131 HKEY ParentKey,
1132 HKEY Key)
1133 {
1134 BLOCK_OFFSET NKBOffset;
1135 PKEY_CELL NewKeyCell;
1136 ULONG KeyCellSize;
1137 USHORT SubKeyCount;
1138 ULONG ValueCount;
1139 PLIST_ENTRY Entry;
1140 HKEY SubKey;
1141 PVALUE Value;
1142
1143 DPRINT ("CmiExportSubKey('%s') called\n", Key->Name);
1144
1145 /* Don't export links */
1146 if (Key->DataType == REG_LINK)
1147 return TRUE;
1148
1149 /* Allocate key cell */
1150 KeyCellSize = sizeof(KEY_CELL) + Key->NameSize - 1;
1151 if (!CmiAllocateCell (Hive, KeyCellSize, (PVOID)&NewKeyCell, &NKBOffset))
1152 {
1153 DPRINT1 ("CmiAllocateBlock() failed\n");
1154 return FALSE;
1155 }
1156
1157 /* Initialize key cell */
1158 NewKeyCell->Id = REG_KEY_CELL_ID;
1159 NewKeyCell->Type = REG_KEY_CELL_TYPE;
1160 NewKeyCell->LastWriteTime = 0;
1161 NewKeyCell->ParentKeyOffset = ParentKeyOffset;
1162 NewKeyCell->NumberOfSubKeys = 0;
1163 NewKeyCell->HashTableOffset = -1;
1164 NewKeyCell->NumberOfValues = 0;
1165 NewKeyCell->ValueListOffset = -1;
1166 NewKeyCell->SecurityKeyOffset = -1;
1167 NewKeyCell->NameSize = Key->NameSize - 1;
1168 NewKeyCell->ClassNameOffset = -1;
1169 memcpy (NewKeyCell->Name,
1170 Key->Name,
1171 Key->NameSize - 1);
1172
1173 /* Add key cell to the parent key's hash table */
1174 if (!CmiAddKeyToParentHashTable (Hive,
1175 ParentKeyOffset,
1176 NewKeyCell,
1177 NKBOffset))
1178 {
1179 DPRINT1 ("CmiAddKeyToParentHashTable() failed\n");
1180 return FALSE;
1181 }
1182
1183 ValueCount = RegGetValueCount (Key);
1184 DPRINT ("ValueCount: %lu\n", ValueCount);
1185 if (ValueCount > 0)
1186 {
1187 /* Allocate value list cell */
1188 CmiAllocateValueListCell (Hive,
1189 &NewKeyCell->ValueListOffset,
1190 ValueCount);
1191
1192 if (Key->DataSize != 0)
1193 {
1194 if (!CmiExportValue (Hive, NKBOffset, Key, NULL))
1195 return FALSE;
1196 }
1197
1198 /* Enumerate values */
1199 Entry = Key->ValueList.Flink;
1200 while (Entry != &Key->ValueList)
1201 {
1202 Value = CONTAINING_RECORD(Entry,
1203 VALUE,
1204 ValueList);
1205
1206 if (!CmiExportValue (Hive, NKBOffset, Key, Value))
1207 return FALSE;
1208
1209 Entry = Entry->Flink;
1210 }
1211 }
1212
1213 SubKeyCount = RegGetSubKeyCount (Key);
1214 DPRINT ("SubKeyCount: %lu\n", SubKeyCount);
1215 if (SubKeyCount > 0)
1216 {
1217 /* Allocate hash table cell */
1218 CmiAllocateHashTableCell (Hive,
1219 &NewKeyCell->HashTableOffset,
1220 SubKeyCount);
1221
1222 /* Enumerate subkeys */
1223 Entry = Key->SubKeyList.Flink;
1224 while (Entry != &Key->SubKeyList)
1225 {
1226 SubKey = CONTAINING_RECORD(Entry,
1227 KEY,
1228 KeyList);
1229
1230 if (!CmiExportSubKey (Hive, NKBOffset, Key, SubKey))
1231 return FALSE;
1232
1233 Entry = Entry->Flink;
1234 }
1235 }
1236
1237 return TRUE;
1238 }
1239
1240
1241 static VOID
1242 CmiCalcHiveChecksum (PREGISTRY_HIVE Hive)
1243 {
1244 PULONG Buffer;
1245 ULONG Sum;
1246 ULONG i;
1247
1248 Buffer = (PULONG)Hive->HiveHeader;
1249 Sum = 0;
1250 for (i = 0; i < 127; i++)
1251 Sum += Buffer[i];
1252
1253 Hive->HiveHeader->Checksum = Sum;
1254 }
1255
1256
1257 BOOL
1258 CmiExportHive (PREGISTRY_HIVE Hive,
1259 PCHAR KeyName)
1260 {
1261 PKEY_CELL KeyCell;
1262 HKEY Key;
1263 USHORT SubKeyCount;
1264 ULONG ValueCount;
1265 PLIST_ENTRY Entry;
1266 HKEY SubKey;
1267 PVALUE Value;
1268
1269 DPRINT ("CmiExportHive(%p, '%s') called\n", Hive, KeyName);
1270
1271 if (RegOpenKey (NULL, KeyName, &Key) != ERROR_SUCCESS)
1272 {
1273 DPRINT1 ("RegOpenKey() failed\n");
1274 return FALSE;
1275 }
1276
1277 DPRINT ("Name: %s\n", KeyName);
1278
1279 KeyCell = CmiGetCell (Hive,
1280 Hive->HiveHeader->RootKeyOffset,
1281 NULL);
1282 if (KeyCell == NULL)
1283 {
1284 DPRINT1 ("CmiGetCell() failed\n");
1285 return FALSE;
1286 }
1287
1288 ValueCount = RegGetValueCount (Key);
1289 DPRINT ("ValueCount: %lu\n", ValueCount);
1290 if (ValueCount > 0)
1291 {
1292 /* Allocate value list cell */
1293 CmiAllocateValueListCell (Hive,
1294 &KeyCell->ValueListOffset,
1295 ValueCount);
1296
1297 if (Key->DataSize != 0)
1298 {
1299 if (!CmiExportValue (Hive, Hive->HiveHeader->RootKeyOffset, Key, NULL))
1300 return FALSE;
1301 }
1302
1303 /* Enumerate values */
1304 Entry = Key->ValueList.Flink;
1305 while (Entry != &Key->ValueList)
1306 {
1307 Value = CONTAINING_RECORD(Entry,
1308 VALUE,
1309 ValueList);
1310
1311 if (!CmiExportValue (Hive, Hive->HiveHeader->RootKeyOffset, Key, Value))
1312 return FALSE;
1313
1314 Entry = Entry->Flink;
1315 }
1316 }
1317
1318 SubKeyCount = RegGetSubKeyCount (Key);
1319 DPRINT ("SubKeyCount: %lu\n", SubKeyCount);
1320 if (SubKeyCount > 0)
1321 {
1322 /* Allocate hash table cell */
1323 CmiAllocateHashTableCell (Hive,
1324 &KeyCell->HashTableOffset,
1325 SubKeyCount);
1326
1327 /* Enumerate subkeys */
1328 Entry = Key->SubKeyList.Flink;
1329 while (Entry != &Key->SubKeyList)
1330 {
1331 SubKey = CONTAINING_RECORD(Entry,
1332 KEY,
1333 KeyList);
1334
1335 if (!CmiExportSubKey (Hive, Hive->HiveHeader->RootKeyOffset, Key, SubKey))
1336 return FALSE;
1337
1338 Entry = Entry->Flink;
1339 }
1340 }
1341
1342 CmiCalcHiveChecksum (Hive);
1343
1344 return TRUE;
1345 }
1346
1347
1348 static BOOL
1349 CmiWriteHive(PREGISTRY_HIVE Hive,
1350 PCHAR FileName)
1351 {
1352 PHBIN Bin;
1353 FILE *File;
1354 ULONG i;
1355
1356 #if 0
1357 /* Check for existing hive file */
1358 File = fopen (FileName, "rb");
1359 if (File != NULL)
1360 {
1361 printf (" File already exists\n");
1362 fclose (File);
1363 return TRUE;
1364 }
1365 #endif
1366
1367 /* Create new hive file */
1368 File = fopen (FileName, "w+b");
1369 if (File == NULL)
1370 {
1371 printf(" Error creating/opening file\n");
1372 return FALSE;
1373 }
1374
1375 fseek (File, 0, SEEK_SET);
1376
1377 /* Calculate header checksum */
1378 CmiCalcHiveChecksum (Hive);
1379
1380 /* Write hive header */
1381 fwrite (Hive->HiveHeader, REG_BLOCK_SIZE, 1, File);
1382
1383 Bin = NULL;
1384 for (i = 0; i < Hive->BlockListSize; i++)
1385 {
1386 if (Hive->BlockList[i] != Bin)
1387 {
1388 Bin = Hive->BlockList[i];
1389
1390 DPRINT ("Bin[%lu]: Offset 0x%lx Size 0x%lx\n",
1391 i, Bin->BinOffset, Bin->BinSize);
1392
1393 fwrite (Bin, Bin->BinSize, 1, File);
1394 }
1395 }
1396
1397 fclose (File);
1398
1399 return TRUE;
1400 }
1401
1402
1403 BOOL
1404 ExportBinaryHive (PCHAR FileName,
1405 PCHAR KeyName)
1406 {
1407 PREGISTRY_HIVE Hive;
1408
1409 printf (" Creating binary hive: %s\n", FileName);
1410
1411 Hive = CmiCreateRegistryHive (KeyName);
1412 if (Hive == NULL)
1413 return FALSE;
1414
1415 if (!CmiExportHive (Hive, KeyName))
1416 {
1417 CmiDestroyRegistryHive (Hive);
1418 return FALSE;
1419 }
1420
1421 if (!CmiWriteHive (Hive, FileName))
1422 {
1423 CmiDestroyRegistryHive (Hive);
1424 return FALSE;
1425 }
1426
1427 CmiDestroyRegistryHive (Hive);
1428
1429 return TRUE;
1430 }
1431
1432 /* EOF */