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