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