update for HEAD-2003091401
[reactos.git] / 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$
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 #if 0
1323   /* Check for existing hive file */
1324   File = fopen (FileName, "rb");
1325   if (File != NULL)
1326     {
1327       printf ("    File already exists\n");
1328       fclose (File);
1329       return TRUE;
1330     }
1331 #endif
1332
1333   /* Create new hive file */
1334   File = fopen (FileName, "w+b");
1335   if (File == NULL)
1336     {
1337       return FALSE;
1338     }
1339
1340   fseek (File, 0, SEEK_SET);
1341
1342   /* Calculate header checksum */
1343   CmiCalcHiveChecksum (Hive);
1344
1345   /* Write hive header */
1346   fwrite (Hive->HiveHeader, REG_BLOCK_SIZE, 1, File);
1347
1348   Bin = NULL;
1349   for (i = 0; i < Hive->BlockListSize; i++)
1350     {
1351       if (Hive->BlockList[i] != Bin)
1352         {
1353           Bin = Hive->BlockList[i];
1354
1355           DPRINT ("Bin[%lu]: Offset 0x%lx  Size 0x%lx\n",
1356                   i, Bin->BlockOffset, Bin->BlockSize);
1357
1358           fwrite (Bin, Bin->BlockSize, 1, File);
1359         }
1360     }
1361
1362   fclose (File);
1363
1364   return TRUE;
1365 }
1366
1367
1368 BOOL
1369 ExportBinaryHive (PCHAR FileName,
1370                   PCHAR KeyName)
1371 {
1372   PREGISTRY_HIVE Hive;
1373
1374   printf ("  Creating binary hive: %s\n", FileName);
1375
1376   Hive = CmiCreateRegistryHive ();
1377   if (Hive == NULL)
1378     return FALSE;
1379
1380   if (!CmiExportHive (Hive, KeyName))
1381     {
1382       CmiDestroyRegistryHive (Hive);
1383       return FALSE;
1384     }
1385
1386   if (!CmiWriteHive (Hive, FileName))
1387     {
1388       CmiDestroyRegistryHive (Hive);
1389       return FALSE;
1390     }
1391
1392   CmiDestroyRegistryHive (Hive);
1393
1394   return TRUE;
1395 }
1396
1397 /* EOF */