Do not overwrite existing hive files.
[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.4 2003/05/18 13:50:58 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 ULONG BlockIndex;
458
459 if (ppBin)
460 *ppBin = NULL;
461
462 if (BlockOffset == (ULONG_PTR) -1)
463 return NULL;
464
465 BlockIndex = BlockOffset / 4096;
466 if (BlockIndex >= Hive->BlockListSize)
467 return NULL;
468
469 pBin = Hive->BlockList[BlockIndex];
470 if (ppBin)
471 *ppBin = pBin;
472
473 return (PVOID)((ULONG_PTR)pBin + (BlockOffset - pBin->BlockOffset));
474 }
475
476
477 static BOOL
478 CmiMergeFree(PREGISTRY_HIVE RegistryHive,
479 PCELL_HEADER FreeBlock,
480 BLOCK_OFFSET FreeOffset)
481 {
482 BLOCK_OFFSET BlockOffset;
483 BLOCK_OFFSET BinOffset;
484 ULONG BlockSize;
485 ULONG BinSize;
486 PHBIN Bin;
487 ULONG i;
488
489 DPRINT("CmiMergeFree(Block %lx Offset %lx Size %lx) called\n",
490 FreeBlock, FreeOffset, FreeBlock->CellSize);
491
492 CmiGetBlock(RegistryHive,
493 FreeOffset,
494 &Bin);
495 DPRINT("Bin %p\n", Bin);
496 if (Bin == NULL)
497 return FALSE;
498
499 BinOffset = Bin->BlockOffset;
500 BinSize = Bin->BlockSize;
501 DPRINT("Bin %p Offset %lx Size %lx\n", Bin, BinOffset, BinSize);
502
503 for (i = 0; i < RegistryHive->FreeListSize; i++)
504 {
505 BlockOffset = RegistryHive->FreeListOffset[i];
506 BlockSize = RegistryHive->FreeList[i]->CellSize;
507 if (BlockOffset > BinOffset &&
508 BlockOffset < BinOffset + BinSize)
509 {
510 DPRINT("Free block: Offset %lx Size %lx\n",
511 BlockOffset, BlockSize);
512
513 if ((i < (RegistryHive->FreeListSize - 1)) &&
514 (BlockOffset + BlockSize == FreeOffset) &&
515 (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
516 {
517 DPRINT("Merge current block with previous and next block\n");
518
519 RegistryHive->FreeList[i]->CellSize +=
520 (FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);
521
522 FreeBlock->CellSize = 0;
523 RegistryHive->FreeList[i + 1]->CellSize = 0;
524
525
526 if ((i + 2) < RegistryHive->FreeListSize)
527 {
528 memmove (&RegistryHive->FreeList[i + 1],
529 &RegistryHive->FreeList[i + 2],
530 sizeof(RegistryHive->FreeList[0])
531 * (RegistryHive->FreeListSize - i - 2));
532 memmove (&RegistryHive->FreeListOffset[i + 1],
533 &RegistryHive->FreeListOffset[i + 2],
534 sizeof(RegistryHive->FreeListOffset[0])
535 * (RegistryHive->FreeListSize - i - 2));
536 }
537 RegistryHive->FreeListSize--;
538
539 return TRUE;
540 }
541 else if (BlockOffset + BlockSize == FreeOffset)
542 {
543 DPRINT("Merge current block with previous block\n");
544
545 RegistryHive->FreeList[i]->CellSize += FreeBlock->CellSize;
546 FreeBlock->CellSize = 0;
547
548 return TRUE;
549 }
550 else if (FreeOffset + FreeBlock->CellSize == BlockOffset)
551 {
552 DPRINT("Merge current block with next block\n");
553
554 FreeBlock->CellSize += RegistryHive->FreeList[i]->CellSize;
555 RegistryHive->FreeList[i]->CellSize = 0;
556 RegistryHive->FreeList[i] = FreeBlock;
557 RegistryHive->FreeListOffset[i] = FreeOffset;
558
559 return TRUE;
560 }
561 }
562 }
563
564 return FALSE;
565 }
566
567
568 static BOOL
569 CmiAddFree(PREGISTRY_HIVE RegistryHive,
570 PCELL_HEADER FreeBlock,
571 BLOCK_OFFSET FreeOffset,
572 BOOL MergeFreeBlocks)
573 {
574 PCELL_HEADER *tmpList;
575 BLOCK_OFFSET *tmpListOffset;
576 LONG minInd;
577 LONG maxInd;
578 LONG medInd;
579
580 assert(RegistryHive);
581 assert(FreeBlock);
582
583 DPRINT("FreeBlock %.08lx FreeOffset %.08lx\n",
584 FreeBlock, FreeOffset);
585
586 /* Merge free blocks */
587 if (MergeFreeBlocks == TRUE)
588 {
589 if (CmiMergeFree(RegistryHive, FreeBlock, FreeOffset))
590 return TRUE;
591 }
592
593 if ((RegistryHive->FreeListSize + 1) > RegistryHive->FreeListMax)
594 {
595 tmpList = malloc (sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax + 32));
596 if (tmpList == NULL)
597 {
598 return FALSE;
599 }
600
601 tmpListOffset = malloc (sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax + 32));
602 if (tmpListOffset == NULL)
603 {
604 free (tmpList);
605 return FALSE;
606 }
607
608 if (RegistryHive->FreeListMax)
609 {
610 memmove (tmpList,
611 RegistryHive->FreeList,
612 sizeof(PCELL_HEADER) * (RegistryHive->FreeListMax));
613 memmove (tmpListOffset,
614 RegistryHive->FreeListOffset,
615 sizeof(BLOCK_OFFSET) * (RegistryHive->FreeListMax));
616 free (RegistryHive->FreeList);
617 free (RegistryHive->FreeListOffset);
618 }
619 RegistryHive->FreeList = tmpList;
620 RegistryHive->FreeListOffset = tmpListOffset;
621 RegistryHive->FreeListMax += 32;
622 }
623
624 /* Add new offset to free list, maintaining list in ascending order */
625 if ((RegistryHive->FreeListSize == 0)
626 || (RegistryHive->FreeListOffset[RegistryHive->FreeListSize-1] < FreeOffset))
627 {
628 /* Add to end of list */
629 RegistryHive->FreeList[RegistryHive->FreeListSize] = FreeBlock;
630 RegistryHive->FreeListOffset[RegistryHive->FreeListSize++] = FreeOffset;
631 }
632 else if (RegistryHive->FreeListOffset[0] > FreeOffset)
633 {
634 /* Add to begin of list */
635 memmove (&RegistryHive->FreeList[1],
636 &RegistryHive->FreeList[0],
637 sizeof(RegistryHive->FreeList[0]) * RegistryHive->FreeListSize);
638 memmove (&RegistryHive->FreeListOffset[1],
639 &RegistryHive->FreeListOffset[0],
640 sizeof(RegistryHive->FreeListOffset[0]) * RegistryHive->FreeListSize);
641 RegistryHive->FreeList[0] = FreeBlock;
642 RegistryHive->FreeListOffset[0] = FreeOffset;
643 RegistryHive->FreeListSize++;
644 }
645 else
646 {
647 /* Search where to insert */
648 minInd = 0;
649 maxInd = RegistryHive->FreeListSize - 1;
650 while ((maxInd - minInd) > 1)
651 {
652 medInd = (minInd + maxInd) / 2;
653 if (RegistryHive->FreeListOffset[medInd] > FreeOffset)
654 maxInd = medInd;
655 else
656 minInd = medInd;
657 }
658
659 /* Insert before maxInd */
660 memmove (&RegistryHive->FreeList[maxInd+1],
661 &RegistryHive->FreeList[maxInd],
662 sizeof(RegistryHive->FreeList[0]) * (RegistryHive->FreeListSize - minInd));
663 memmove (&RegistryHive->FreeListOffset[maxInd + 1],
664 &RegistryHive->FreeListOffset[maxInd],
665 sizeof(RegistryHive->FreeListOffset[0]) * (RegistryHive->FreeListSize-minInd));
666 RegistryHive->FreeList[maxInd] = FreeBlock;
667 RegistryHive->FreeListOffset[maxInd] = FreeOffset;
668 RegistryHive->FreeListSize++;
669 }
670
671 return TRUE;
672 }
673
674
675 static BOOL
676 CmiAddBin(PREGISTRY_HIVE RegistryHive,
677 PVOID *NewBlock,
678 PBLOCK_OFFSET NewBlockOffset)
679 {
680 PCELL_HEADER tmpBlock;
681 PHBIN * tmpBlockList;
682 PHBIN tmpBin;
683
684 tmpBin = malloc (REG_BLOCK_SIZE);
685 if (tmpBin == NULL)
686 {
687 return FALSE;
688 }
689 memset (tmpBin, 0, REG_BLOCK_SIZE);
690
691 tmpBin->BlockId = REG_BIN_ID;
692 tmpBin->BlockOffset = RegistryHive->FileSize - REG_BLOCK_SIZE;
693 RegistryHive->FileSize += REG_BLOCK_SIZE;
694 tmpBin->BlockSize = REG_BLOCK_SIZE;
695 tmpBin->Unused1 = 0;
696 tmpBin->DateModified = 0ULL;
697 tmpBin->Unused2 = 0;
698
699 /* Increase size of list of blocks */
700 tmpBlockList = malloc (sizeof(PHBIN) * (RegistryHive->BlockListSize + 1));
701 if (tmpBlockList == NULL)
702 {
703 free (tmpBin);
704 return FALSE;
705 }
706
707 if (RegistryHive->BlockListSize > 0)
708 {
709 memcpy (tmpBlockList,
710 RegistryHive->BlockList,
711 sizeof(PHBIN) * RegistryHive->BlockListSize);
712 free (RegistryHive->BlockList);
713 }
714
715 RegistryHive->BlockList = tmpBlockList;
716 RegistryHive->BlockList[RegistryHive->BlockListSize++] = tmpBin;
717
718 /* Initialize a free block in this heap : */
719 tmpBlock = (PCELL_HEADER)((ULONG_PTR) tmpBin + REG_HBIN_DATA_OFFSET);
720 tmpBlock->CellSize = (REG_BLOCK_SIZE - REG_HBIN_DATA_OFFSET);
721
722 *NewBlock = (PVOID) tmpBlock;
723
724 if (NewBlockOffset)
725 *NewBlockOffset = tmpBin->BlockOffset + REG_HBIN_DATA_OFFSET;
726
727 return TRUE;
728 }
729
730
731 static BOOL
732 CmiAllocateBlock(PREGISTRY_HIVE RegistryHive,
733 PVOID *Block,
734 LONG BlockSize,
735 PBLOCK_OFFSET pBlockOffset)
736 {
737 PCELL_HEADER NewBlock;
738 PHBIN pBin;
739 ULONG i;
740
741 *Block = NULL;
742
743 /* Round to 16 bytes multiple */
744 BlockSize = (BlockSize + sizeof(ULONG) + 15) & 0xfffffff0;
745
746 /* first search in free blocks */
747 NewBlock = NULL;
748 for (i = 0; i < RegistryHive->FreeListSize; i++)
749 {
750 if (RegistryHive->FreeList[i]->CellSize >= BlockSize)
751 {
752 NewBlock = RegistryHive->FreeList[i];
753 if (pBlockOffset)
754 *pBlockOffset = RegistryHive->FreeListOffset[i];
755
756 if ((i + 1) < RegistryHive->FreeListSize)
757 {
758 memmove (&RegistryHive->FreeList[i],
759 &RegistryHive->FreeList[i + 1],
760 sizeof(RegistryHive->FreeList[0])
761 * (RegistryHive->FreeListSize - i - 1));
762 memmove (&RegistryHive->FreeListOffset[i],
763 &RegistryHive->FreeListOffset[i + 1],
764 sizeof(RegistryHive->FreeListOffset[0])
765 * (RegistryHive->FreeListSize - i - 1));
766 }
767 RegistryHive->FreeListSize--;
768 break;
769 }
770 }
771
772 /* Need to extend hive file : */
773 if (NewBlock == NULL)
774 {
775 /* Add a new block */
776 if (!CmiAddBin(RegistryHive, (PVOID *)&NewBlock , pBlockOffset))
777 return FALSE;
778 }
779
780 *Block = NewBlock;
781
782 /* Split the block in two parts */
783 if (NewBlock->CellSize > BlockSize)
784 {
785 NewBlock = (PCELL_HEADER) ((ULONG_PTR) NewBlock+BlockSize);
786 NewBlock->CellSize = ((PCELL_HEADER) (*Block))->CellSize - BlockSize;
787 CmiAddFree (RegistryHive,
788 NewBlock,
789 *pBlockOffset + BlockSize,
790 TRUE);
791 }
792 else if (NewBlock->CellSize < BlockSize)
793 {
794 return FALSE;
795 }
796
797 memset(*Block, 0, BlockSize);
798 ((PCELL_HEADER)(*Block))->CellSize = -BlockSize;
799
800 return TRUE;
801 }
802
803
804 static BOOL
805 CmiAllocateHashTableCell (PREGISTRY_HIVE Hive,
806 PBLOCK_OFFSET HBOffset,
807 ULONG SubKeyCount)
808 {
809 PHASH_TABLE_CELL HashCell;
810 ULONG NewHashSize;
811 BOOL Status;
812
813 NewHashSize = ROUND_UP(sizeof(HASH_TABLE_CELL) +
814 (SubKeyCount - 1) * sizeof(HASH_RECORD),
815 0x10);
816 Status = CmiAllocateBlock (Hive,
817 (PVOID*) &HashCell,
818 NewHashSize,
819 HBOffset);
820 if ((HashCell == NULL) || (Status == FALSE))
821 {
822 return FALSE;
823 }
824
825 HashCell->Id = REG_HASH_TABLE_BLOCK_ID;
826 HashCell->HashTableSize = SubKeyCount;
827
828 return TRUE;
829 }
830
831
832 static BOOL
833 CmiAddKeyToParentHashTable (PREGISTRY_HIVE Hive,
834 BLOCK_OFFSET ParentKeyOffset,
835 PKEY_CELL NewKeyCell,
836 BLOCK_OFFSET NKBOffset)
837 {
838 PHASH_TABLE_CELL HashBlock;
839 PKEY_CELL ParentKeyCell;
840 ULONG i;
841
842 ParentKeyCell = CmiGetBlock (Hive,
843 ParentKeyOffset,
844 NULL);
845 if (ParentKeyCell == NULL)
846 {
847 DPRINT1 ("CmiGetBlock() failed\n");
848 return FALSE;
849 }
850
851 HashBlock =CmiGetBlock (Hive,
852 ParentKeyCell->HashTableOffset,
853 NULL);
854 if (HashBlock == NULL)
855 {
856 DPRINT1 ("CmiGetBlock() failed\n");
857 return FALSE;
858 }
859
860 for (i = 0; i < HashBlock->HashTableSize; i++)
861 {
862 if (HashBlock->Table[i].KeyOffset == 0)
863 {
864 HashBlock->Table[i].KeyOffset = NKBOffset;
865 memcpy (&HashBlock->Table[i].HashValue,
866 NewKeyCell->Name,
867 4);
868 ParentKeyCell->NumberOfSubKeys++;
869 return TRUE;
870 }
871 }
872
873 return FALSE;
874 }
875
876
877 static BOOL
878 CmiAllocateValueListCell (PREGISTRY_HIVE Hive,
879 PBLOCK_OFFSET ValueListOffset,
880 ULONG ValueCount)
881 {
882 PVALUE_LIST_CELL ValueListCell;
883 ULONG ValueListSize;
884 BOOL Status;
885
886 ValueListSize = ROUND_UP (ValueCount * sizeof(BLOCK_OFFSET),
887 0x10);
888 Status = CmiAllocateBlock (Hive,
889 (PVOID)&ValueListCell,
890 ValueListSize,
891 ValueListOffset);
892 if ((ValueListCell == NULL) || (Status == FALSE))
893 {
894 DPRINT1 ("CmiAllocateBlock() failed\n");
895 return FALSE;
896 }
897
898 return TRUE;
899 }
900
901
902 static BOOL
903 CmiAllocateValueCell(PREGISTRY_HIVE Hive,
904 PVALUE_CELL *ValueCell,
905 BLOCK_OFFSET *ValueCellOffset,
906 PCHAR ValueName)
907 {
908 PVALUE_CELL NewValueCell;
909 ULONG NameSize;
910 BOOL Status;
911
912 NameSize = (ValueName == NULL) ? 0 : strlen (ValueName);
913 Status = CmiAllocateBlock(Hive,
914 (PVOID*)&NewValueCell,
915 sizeof(VALUE_CELL) + NameSize,
916 ValueCellOffset);
917 if ((NewValueCell == NULL) || (Status == FALSE))
918 {
919 DPRINT1 ("CmiAllocateBlock() failed\n");
920 return FALSE;
921 }
922
923 NewValueCell->Id = REG_VALUE_CELL_ID;
924 NewValueCell->NameSize = NameSize;
925 if (NameSize > 0)
926 {
927 memcpy (NewValueCell->Name,
928 ValueName,
929 NameSize);
930 NewValueCell->Flags = REG_VALUE_NAME_PACKED;
931 }
932 NewValueCell->DataType = 0;
933 NewValueCell->DataSize = 0;
934 NewValueCell->DataOffset = -1;
935
936 *ValueCell = NewValueCell;
937
938 return TRUE;
939 }
940
941
942 static BOOL
943 CmiAddValueToKeyValueList(PREGISTRY_HIVE Hive,
944 BLOCK_OFFSET KeyCellOffset,
945 BLOCK_OFFSET ValueCellOffset)
946 {
947 PVALUE_LIST_CELL ValueListCell;
948 PKEY_CELL KeyCell;
949
950 KeyCell = CmiGetBlock (Hive, KeyCellOffset, NULL);
951 if (KeyCell == NULL)
952 {
953 DPRINT1 ("CmiGetBlock() failed\n");
954 return FALSE;
955 }
956
957 ValueListCell = CmiGetBlock (Hive, KeyCell->ValuesOffset, NULL);
958 if (ValueListCell == NULL)
959 {
960 DPRINT1 ("CmiGetBlock() failed\n");
961 return FALSE;
962 }
963
964 ValueListCell->Values[KeyCell->NumberOfValues] = ValueCellOffset;
965 KeyCell->NumberOfValues++;
966
967 return TRUE;
968 }
969
970
971 static VOID
972 memexpand (PWCHAR Dst,
973 PCHAR Src,
974 ULONG Length)
975 {
976 ULONG i;
977
978 for (i = 0; i < Length; i++)
979 Dst[i] = (WCHAR)Src[i];
980 }
981
982
983 static BOOL
984 CmiExportValue (PREGISTRY_HIVE Hive,
985 BLOCK_OFFSET KeyCellOffset,
986 HKEY Key,
987 PVALUE Value)
988 {
989 BLOCK_OFFSET ValueCellOffset;
990 BLOCK_OFFSET DataCellOffset;
991 PVALUE_CELL ValueCell;
992 PDATA_CELL DataCell;
993 ULONG SrcDataSize;
994 ULONG DstDataSize;
995 ULONG DataType;
996 PUCHAR Data;
997 BOOL Expand = FALSE;
998
999 DPRINT ("CmiExportValue('%s') called\n", (Value == NULL) ? "<default>" : (PCHAR)Value->Name);
1000 DPRINT ("DataSize %lu\n", (Value == NULL) ? Key->DataSize : Value->DataSize);
1001
1002 /* Allocate value cell */
1003 if (!CmiAllocateValueCell(Hive, &ValueCell, &ValueCellOffset, (Value == NULL) ? NULL : Value->Name))
1004 {
1005 return FALSE;
1006 }
1007
1008 if (!CmiAddValueToKeyValueList(Hive, KeyCellOffset, ValueCellOffset))
1009 {
1010 return FALSE;
1011 }
1012
1013 if (Value == NULL)
1014 {
1015 DataType = Key->DataType;
1016 SrcDataSize = Key->DataSize;
1017 Data = Key->Data;
1018 }
1019 else
1020 {
1021 DataType = Value->DataType;
1022 SrcDataSize = Value->DataSize;
1023 Data = Value->Data;
1024 }
1025
1026 DstDataSize = SrcDataSize;
1027 if (DataType == REG_SZ ||
1028 DataType == REG_EXPAND_SZ ||
1029 DataType == REG_MULTI_SZ)
1030 {
1031 DstDataSize *= sizeof(WCHAR);
1032 Expand = TRUE;
1033 }
1034
1035 if (DstDataSize <= sizeof(BLOCK_OFFSET))
1036 {
1037 ValueCell->DataSize = DstDataSize | 0x80000000;
1038 ValueCell->DataType = DataType;
1039 if (Expand)
1040 {
1041 memexpand ((PWCHAR)&ValueCell->DataOffset,
1042 (PCHAR)&Data,
1043 SrcDataSize);
1044 }
1045 else
1046 {
1047 memcpy (&ValueCell->DataOffset,
1048 &Data,
1049 SrcDataSize);
1050 }
1051 }
1052 else
1053 {
1054 if (!CmiAllocateBlock (Hive,
1055 (PVOID *)&DataCell,
1056 DstDataSize,
1057 &DataCellOffset))
1058 {
1059 return FALSE;
1060 }
1061
1062 ValueCell->DataOffset = DataCellOffset;
1063 ValueCell->DataSize = DstDataSize;
1064 ValueCell->DataType = DataType;
1065
1066 if (Expand)
1067 {
1068 if (SrcDataSize <= sizeof(BLOCK_OFFSET))
1069 {
1070 memexpand ((PWCHAR)DataCell->Data,
1071 (PCHAR)&Data,
1072 SrcDataSize);
1073 }
1074 else
1075 {
1076 memexpand ((PWCHAR)DataCell->Data,
1077 Data,
1078 SrcDataSize);
1079 }
1080 }
1081 else
1082 {
1083 memcpy (DataCell->Data,
1084 Data,
1085 SrcDataSize);
1086 }
1087 }
1088
1089 return TRUE;
1090 }
1091
1092
1093 static BOOL
1094 CmiExportSubKey (PREGISTRY_HIVE Hive,
1095 BLOCK_OFFSET ParentKeyOffset,
1096 HKEY ParentKey,
1097 HKEY Key)
1098 {
1099 BLOCK_OFFSET NKBOffset;
1100 PKEY_CELL NewKeyCell;
1101 ULONG KeyCellSize;
1102 ULONG SubKeyCount;
1103 ULONG ValueCount;
1104 PLIST_ENTRY Entry;
1105 HKEY SubKey;
1106 PVALUE Value;
1107
1108 DPRINT ("CmiExportSubKey('%s') called\n", Key->Name);
1109
1110 /* Don't export links */
1111 if (Key->DataType == REG_LINK)
1112 return TRUE;
1113
1114 /* Allocate key cell */
1115 KeyCellSize = sizeof(KEY_CELL) + Key->NameSize - 1;
1116 if (!CmiAllocateBlock (Hive, (PVOID)&NewKeyCell, KeyCellSize, &NKBOffset))
1117 {
1118 DPRINT1 ("CmiAllocateBlock() failed\n");
1119 return FALSE;
1120 }
1121
1122 /* Initialize key cell */
1123 NewKeyCell->Id = REG_KEY_CELL_ID;
1124 NewKeyCell->Type = REG_KEY_CELL_TYPE;
1125 NewKeyCell->LastWriteTime = 0ULL;
1126 NewKeyCell->ParentKeyOffset = ParentKeyOffset;
1127 NewKeyCell->NumberOfSubKeys = 0;
1128 NewKeyCell->HashTableOffset = -1;
1129 NewKeyCell->NumberOfValues = 0;
1130 NewKeyCell->ValuesOffset = -1;
1131 NewKeyCell->SecurityKeyOffset = -1;
1132 NewKeyCell->NameSize = Key->NameSize - 1;
1133 NewKeyCell->ClassNameOffset = -1;
1134 memcpy (NewKeyCell->Name,
1135 Key->Name,
1136 Key->NameSize - 1);
1137
1138 /* Add key cell to the parent key's hash table */
1139 if (!CmiAddKeyToParentHashTable (Hive,
1140 ParentKeyOffset,
1141 NewKeyCell,
1142 NKBOffset))
1143 {
1144 DPRINT1 ("CmiAddKeyToParentHashTable() failed\n");
1145 return FALSE;
1146 }
1147
1148 ValueCount = RegGetValueCount (Key);
1149 DPRINT ("ValueCount: %lu\n", ValueCount);
1150 if (ValueCount > 0)
1151 {
1152 /* Allocate value list cell */
1153 CmiAllocateValueListCell (Hive,
1154 &NewKeyCell->ValuesOffset,
1155 ValueCount);
1156
1157 if (Key->DataSize != 0)
1158 {
1159 if (!CmiExportValue (Hive, NKBOffset, Key, NULL))
1160 return FALSE;
1161 }
1162
1163 /* Enumerate values */
1164 Entry = Key->ValueList.Flink;
1165 while (Entry != &Key->ValueList)
1166 {
1167 Value = CONTAINING_RECORD(Entry,
1168 VALUE,
1169 ValueList);
1170
1171 if (!CmiExportValue (Hive, NKBOffset, Key, Value))
1172 return FALSE;
1173
1174 Entry = Entry->Flink;
1175 }
1176 }
1177
1178 SubKeyCount = RegGetSubKeyCount (Key);
1179 DPRINT ("SubKeyCount: %lu\n", SubKeyCount);
1180 if (SubKeyCount > 0)
1181 {
1182 /* Allocate hash table cell */
1183 CmiAllocateHashTableCell (Hive,
1184 &NewKeyCell->HashTableOffset,
1185 SubKeyCount);
1186
1187 /* Enumerate subkeys */
1188 Entry = Key->SubKeyList.Flink;
1189 while (Entry != &Key->SubKeyList)
1190 {
1191 SubKey = CONTAINING_RECORD(Entry,
1192 KEY,
1193 KeyList);
1194
1195 if (!CmiExportSubKey (Hive, NKBOffset, Key, SubKey))
1196 return FALSE;
1197
1198 Entry = Entry->Flink;
1199 }
1200 }
1201
1202 return TRUE;
1203 }
1204
1205
1206 static VOID
1207 CmiCalcHiveChecksum (PREGISTRY_HIVE Hive)
1208 {
1209 PULONG Buffer;
1210 ULONG Sum;
1211 ULONG i;
1212
1213 Buffer = (PULONG)Hive->HiveHeader;
1214 Sum = 0;
1215 for (i = 0; i < 127; i++)
1216 Sum += Buffer[i];
1217
1218 Hive->HiveHeader->Checksum = Sum;
1219 }
1220
1221
1222 BOOL
1223 CmiExportHive (PREGISTRY_HIVE Hive,
1224 PCHAR KeyName)
1225 {
1226 PKEY_CELL KeyCell;
1227 HKEY Key;
1228 ULONG i;
1229 ULONG SubKeyCount;
1230 ULONG ValueCount;
1231 PLIST_ENTRY Entry;
1232 HKEY SubKey;
1233 PVALUE Value;
1234
1235 DPRINT ("CmiExportHive(%p, '%s') called\n", Hive, KeyName);
1236
1237 if (RegOpenKey (NULL, KeyName, &Key) != ERROR_SUCCESS)
1238 {
1239 DPRINT1 ("RegOpenKey() failed\n");
1240 return FALSE;
1241 }
1242
1243 DPRINT ("Name: %s\n", KeyName);
1244
1245 KeyCell = CmiGetBlock (Hive,
1246 Hive->HiveHeader->RootKeyCell,
1247 NULL);
1248 if (KeyCell == NULL)
1249 {
1250 DPRINT1 ("CmiGetBlock() failed\n");
1251 return FALSE;
1252 }
1253
1254 ValueCount = RegGetValueCount (Key);
1255 DPRINT ("ValueCount: %lu\n", ValueCount);
1256 if (ValueCount > 0)
1257 {
1258 /* Allocate value list cell */
1259 CmiAllocateValueListCell (Hive,
1260 &KeyCell->ValuesOffset,
1261 ValueCount);
1262
1263 if (Key->DataSize != 0)
1264 {
1265 if (!CmiExportValue (Hive, Hive->HiveHeader->RootKeyCell, Key, NULL))
1266 return FALSE;
1267 }
1268
1269 /* Enumerate values */
1270 Entry = Key->ValueList.Flink;
1271 while (Entry != &Key->ValueList)
1272 {
1273 Value = CONTAINING_RECORD(Entry,
1274 VALUE,
1275 ValueList);
1276
1277 if (!CmiExportValue (Hive, Hive->HiveHeader->RootKeyCell, Key, Value))
1278 return FALSE;
1279
1280 Entry = Entry->Flink;
1281 }
1282 }
1283
1284 SubKeyCount = RegGetSubKeyCount (Key);
1285 DPRINT ("SubKeyCount: %lu\n", SubKeyCount);
1286 if (SubKeyCount > 0)
1287 {
1288 /* Allocate hash table cell */
1289 CmiAllocateHashTableCell (Hive,
1290 &KeyCell->HashTableOffset,
1291 SubKeyCount);
1292
1293 /* Enumerate subkeys */
1294 Entry = Key->SubKeyList.Flink;
1295 while (Entry != &Key->SubKeyList)
1296 {
1297 SubKey = CONTAINING_RECORD(Entry,
1298 KEY,
1299 KeyList);
1300
1301 if (!CmiExportSubKey (Hive, Hive->HiveHeader->RootKeyCell, Key, SubKey))
1302 return FALSE;
1303
1304 Entry = Entry->Flink;
1305 }
1306 }
1307
1308 CmiCalcHiveChecksum (Hive);
1309
1310 return TRUE;
1311 }
1312
1313
1314 static BOOL
1315 CmiWriteHive(PREGISTRY_HIVE Hive,
1316 PCHAR FileName)
1317 {
1318 PHBIN Bin;
1319 FILE *File;
1320 ULONG i;
1321
1322 /* Check for existing hive file */
1323 File = fopen (FileName, "rb");
1324 if (File != NULL)
1325 {
1326 printf (" File already exists\n");
1327 fclose (File);
1328 return TRUE;
1329 }
1330
1331 /* Create new hive file */
1332 File = fopen (FileName, "w+b");
1333 if (File == NULL)
1334 {
1335 return FALSE;
1336 }
1337
1338 fseek (File, 0, SEEK_SET);
1339
1340 /* Calculate header checksum */
1341 CmiCalcHiveChecksum (Hive);
1342
1343 /* Write hive header */
1344 fwrite (Hive->HiveHeader, REG_BLOCK_SIZE, 1, File);
1345
1346 Bin = NULL;
1347 for (i = 0; i < Hive->BlockListSize; i++)
1348 {
1349 if (Hive->BlockList[i] != Bin)
1350 {
1351 Bin = Hive->BlockList[i];
1352
1353 DPRINT ("Bin[%lu]: Offset 0x%lx Size 0x%lx\n",
1354 i, Bin->BlockOffset, Bin->BlockSize);
1355
1356 fwrite (Bin, Bin->BlockSize, 1, File);
1357 }
1358 }
1359
1360 fclose (File);
1361
1362 return TRUE;
1363 }
1364
1365
1366 BOOL
1367 ExportBinaryHive (PCHAR FileName,
1368 PCHAR KeyName)
1369 {
1370 PREGISTRY_HIVE Hive;
1371
1372 printf (" Creating binary hive: %s\n", FileName);
1373
1374 Hive = CmiCreateRegistryHive ();
1375 if (Hive == NULL)
1376 return FALSE;
1377
1378 if (!CmiExportHive (Hive, KeyName))
1379 {
1380 CmiDestroyRegistryHive (Hive);
1381 return FALSE;
1382 }
1383
1384 if (!CmiWriteHive (Hive, FileName))
1385 {
1386 CmiDestroyRegistryHive (Hive);
1387 return FALSE;
1388 }
1389
1390 CmiDestroyRegistryHive (Hive);
1391
1392 return TRUE;
1393 }
1394
1395 /* EOF */