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