s3-misc: Move smb_io_time() to regfio.c
[amitay/samba.git] / source3 / registry / regfio.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Windows NT registry I/O library
4  * Copyright (c) Gerald (Jerry) Carter               2005
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.  
18  */
19
20 #include "includes.h"
21 #include "regfio.h"
22 #include "reg_objects.h"
23 #include "../librpc/gen_ndr/ndr_security.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_REGISTRY
27
28 /*******************************************************************
29  *
30  * TODO : Right now this code basically ignores classnames.
31  *
32  ******************************************************************/
33
34 /*******************************************************************
35  Reads or writes an NTTIME structure.
36 ********************************************************************/
37
38 static bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
39 {
40         uint32 low, high;
41         if (nttime == NULL)
42                 return False;
43
44         prs_debug(ps, depth, desc, "smb_io_time");
45         depth++;
46
47         if(!prs_align(ps))
48                 return False;
49
50         if (MARSHALLING(ps)) {
51                 low = *nttime & 0xFFFFFFFF;
52                 high = *nttime >> 32;
53         }
54
55         if(!prs_uint32("low ", ps, depth, &low)) /* low part */
56                 return False;
57         if(!prs_uint32("high", ps, depth, &high)) /* high part */
58                 return False;
59
60         if (UNMARSHALLING(ps)) {
61                 *nttime = (((uint64_t)high << 32) + low);
62         }
63
64         return True;
65 }
66
67 /*******************************************************************
68 *******************************************************************/
69
70 static int write_block( REGF_FILE *file, prs_struct *ps, uint32 offset )
71 {
72         int bytes_written, returned;
73         char *buffer = prs_data_p( ps );
74         uint32 buffer_size = prs_data_size( ps );
75         SMB_STRUCT_STAT sbuf;
76
77         if ( file->fd == -1 )
78                 return -1;
79
80         /* check for end of file */
81
82         if (sys_fstat(file->fd, &sbuf, false)) {
83                 DEBUG(0,("write_block: stat() failed! (%s)\n", strerror(errno)));
84                 return -1;
85         }
86
87         if ( lseek( file->fd, offset, SEEK_SET ) == -1 ) {
88                 DEBUG(0,("write_block: lseek() failed! (%s)\n", strerror(errno) ));
89                 return -1;
90         }
91         
92         bytes_written = returned = 0;
93         while ( bytes_written < buffer_size ) {
94                 if ( (returned = write( file->fd, buffer+bytes_written, buffer_size-bytes_written )) == -1 ) {
95                         DEBUG(0,("write_block: write() failed! (%s)\n", strerror(errno) ));
96                         return False;
97                 }
98                                 
99                 bytes_written += returned;
100         }
101         
102         return bytes_written;
103 }
104
105 /*******************************************************************
106 *******************************************************************/
107
108 static int read_block( REGF_FILE *file, prs_struct *ps, uint32 file_offset, uint32 block_size )
109 {
110         int bytes_read, returned;
111         char *buffer;
112         SMB_STRUCT_STAT sbuf;
113
114         /* check for end of file */
115
116         if (sys_fstat(file->fd, &sbuf, false)) {
117                 DEBUG(0,("read_block: stat() failed! (%s)\n", strerror(errno)));
118                 return -1;
119         }
120
121         if ( (size_t)file_offset >= sbuf.st_ex_size )
122                 return -1;
123         
124         /* if block_size == 0, we are parsing HBIN records and need 
125            to read some of the header to get the block_size from there */
126            
127         if ( block_size == 0 ) {
128                 char hdr[0x20];
129
130                 if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
131                         DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));
132                         return -1;
133                 }
134
135                 returned = read( file->fd, hdr, 0x20 );
136                 if ( (returned == -1) || (returned < 0x20) ) {
137                         DEBUG(0,("read_block: failed to read in HBIN header. Is the file corrupt?\n"));
138                         return -1;
139                 }
140
141                 /* make sure this is an hbin header */
142
143                 if ( strncmp( hdr, "hbin", HBIN_HDR_SIZE ) != 0 ) {
144                         DEBUG(0,("read_block: invalid block header!\n"));
145                         return -1;
146                 }
147
148                 block_size = IVAL( hdr, 0x08 );
149         }
150
151         DEBUG(10,("read_block: block_size == 0x%x\n", block_size ));
152
153         /* set the offset, initialize the buffer, and read the block from disk */
154
155         if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
156                 DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));
157                 return -1;
158         }
159         
160         if (!prs_init( ps, block_size, file->mem_ctx, UNMARSHALL )) {
161                 DEBUG(0,("read_block: prs_init() failed! (%s)\n", strerror(errno) ));
162                 return -1;
163         }
164         buffer = prs_data_p( ps );
165         bytes_read = returned = 0;
166
167         while ( bytes_read < block_size ) {
168                 if ( (returned = read( file->fd, buffer+bytes_read, block_size-bytes_read )) == -1 ) {
169                         DEBUG(0,("read_block: read() failed (%s)\n", strerror(errno) ));
170                         return False;
171                 }
172                 if ( (returned == 0) && (bytes_read < block_size) ) {
173                         DEBUG(0,("read_block: not a vald registry file ?\n" ));
174                         return False;
175                 }       
176                 
177                 bytes_read += returned;
178         }
179         
180         return bytes_read;
181 }
182
183 /*******************************************************************
184 *******************************************************************/
185
186 static bool write_hbin_block( REGF_FILE *file, REGF_HBIN *hbin )
187 {
188         if ( !hbin->dirty )
189                 return True;
190
191         /* write free space record if any is available */
192
193         if ( hbin->free_off != REGF_OFFSET_NONE ) {
194                 uint32 header = 0xffffffff;
195
196                 if ( !prs_set_offset( &hbin->ps, hbin->free_off-sizeof(uint32) ) )
197                         return False;
198                 if ( !prs_uint32( "free_size", &hbin->ps, 0, &hbin->free_size ) )
199                         return False;
200                 if ( !prs_uint32( "free_header", &hbin->ps, 0, &header ) )
201                         return False;
202         }
203
204         hbin->dirty = (write_block( file, &hbin->ps, hbin->file_off ) != -1);
205
206         return hbin->dirty;
207 }
208
209 /*******************************************************************
210 *******************************************************************/
211
212 static bool hbin_block_close( REGF_FILE *file, REGF_HBIN *hbin )
213 {
214         REGF_HBIN *p;
215
216         /* remove the block from the open list and flush it to disk */
217
218         for ( p=file->block_list; p && p!=hbin; p=p->next )
219                 ;;
220
221         if ( p == hbin ) {
222                 DLIST_REMOVE( file->block_list, hbin );
223         }
224         else
225                 DEBUG(0,("hbin_block_close: block not in open list!\n"));
226
227         if ( !write_hbin_block( file, hbin ) )
228                 return False;
229
230         return True;
231 }
232
233 /*******************************************************************
234 *******************************************************************/
235
236 static bool prs_regf_block( const char *desc, prs_struct *ps, int depth, REGF_FILE *file )
237 {
238         prs_debug(ps, depth, desc, "prs_regf_block");
239         depth++;
240         
241         if ( !prs_uint8s( True, "header", ps, depth, (uint8*)file->header, sizeof( file->header )) )
242                 return False;
243         
244         /* yes, these values are always identical so store them only once */
245         
246         if ( !prs_uint32( "unknown1", ps, depth, &file->unknown1 ))
247                 return False;
248         if ( !prs_uint32( "unknown1 (again)", ps, depth, &file->unknown1 ))
249                 return False;
250
251         /* get the modtime */
252         
253         if ( !prs_set_offset( ps, 0x0c ) )
254                 return False;
255         if ( !smb_io_time( "modtime", &file->mtime, ps, depth ) )
256                 return False;
257
258         /* constants */
259         
260         if ( !prs_uint32( "unknown2", ps, depth, &file->unknown2 ))
261                 return False;
262         if ( !prs_uint32( "unknown3", ps, depth, &file->unknown3 ))
263                 return False;
264         if ( !prs_uint32( "unknown4", ps, depth, &file->unknown4 ))
265                 return False;
266         if ( !prs_uint32( "unknown5", ps, depth, &file->unknown5 ))
267                 return False;
268
269         /* get file offsets */
270         
271         if ( !prs_set_offset( ps, 0x24 ) )
272                 return False;
273         if ( !prs_uint32( "data_offset", ps, depth, &file->data_offset ))
274                 return False;
275         if ( !prs_uint32( "last_block", ps, depth, &file->last_block ))
276                 return False;
277                 
278         /* one more constant */
279         
280         if ( !prs_uint32( "unknown6", ps, depth, &file->unknown6 ))
281                 return False;
282                 
283         /* get the checksum */
284         
285         if ( !prs_set_offset( ps, 0x01fc ) )
286                 return False;
287         if ( !prs_uint32( "checksum", ps, depth, &file->checksum ))
288                 return False;
289         
290         return True;
291 }
292
293 /*******************************************************************
294 *******************************************************************/
295
296 static bool prs_hbin_block( const char *desc, prs_struct *ps, int depth, REGF_HBIN *hbin )
297 {
298         uint32 block_size2;
299
300         prs_debug(ps, depth, desc, "prs_regf_block");
301         depth++;
302         
303         if ( !prs_uint8s( True, "header", ps, depth, (uint8*)hbin->header, sizeof( hbin->header )) )
304                 return False;
305
306         if ( !prs_uint32( "first_hbin_off", ps, depth, &hbin->first_hbin_off ))
307                 return False;
308
309         /* The dosreg.cpp comments say that the block size is at 0x1c.
310            According to a WINXP NTUSER.dat file, this is wrong.  The block_size
311            is at 0x08 */
312
313         if ( !prs_uint32( "block_size", ps, depth, &hbin->block_size ))
314                 return False;
315
316         block_size2 = hbin->block_size;
317         prs_set_offset( ps, 0x1c );
318         if ( !prs_uint32( "block_size2", ps, depth, &block_size2 ))
319                 return False;
320
321         if ( MARSHALLING(ps) )
322                 hbin->dirty = True;
323         
324
325         return True;
326 }
327
328 /*******************************************************************
329 *******************************************************************/
330
331 static bool prs_nk_rec( const char *desc, prs_struct *ps, int depth, REGF_NK_REC *nk )
332 {
333         uint16 class_length, name_length;
334         uint32 start;
335         uint32 data_size, start_off, end_off;
336         uint32 unknown_off = REGF_OFFSET_NONE;
337
338         nk->hbin_off = prs_offset( ps );
339         start = nk->hbin_off;
340         
341         prs_debug(ps, depth, desc, "prs_nk_rec");
342         depth++;
343         
344         /* back up and get the data_size */
345         
346         if ( !prs_set_offset( ps, prs_offset(ps)-sizeof(uint32)) )
347                 return False;
348         start_off = prs_offset( ps );
349         if ( !prs_uint32( "rec_size", ps, depth, &nk->rec_size ))
350                 return False;
351         
352         if ( !prs_uint8s( True, "header", ps, depth, (uint8*)nk->header, sizeof( nk->header )) )
353                 return False;
354                 
355         if ( !prs_uint16( "key_type", ps, depth, &nk->key_type ))
356                 return False;
357         if ( !smb_io_time( "mtime", &nk->mtime, ps, depth ))
358                 return False;
359                 
360         if ( !prs_set_offset( ps, start+0x0010 ) )
361                 return False;
362         if ( !prs_uint32( "parent_off", ps, depth, &nk->parent_off ))
363                 return False;
364         if ( !prs_uint32( "num_subkeys", ps, depth, &nk->num_subkeys ))
365                 return False;
366                 
367         if ( !prs_set_offset( ps, start+0x001c ) )
368                 return False;
369         if ( !prs_uint32( "subkeys_off", ps, depth, &nk->subkeys_off ))
370                 return False;
371         if ( !prs_uint32( "unknown_off", ps, depth, &unknown_off) )
372                 return False;
373                 
374         if ( !prs_set_offset( ps, start+0x0024 ) )
375                 return False;
376         if ( !prs_uint32( "num_values", ps, depth, &nk->num_values ))
377                 return False;
378         if ( !prs_uint32( "values_off", ps, depth, &nk->values_off ))
379                 return False;
380         if ( !prs_uint32( "sk_off", ps, depth, &nk->sk_off ))
381                 return False;
382         if ( !prs_uint32( "classname_off", ps, depth, &nk->classname_off ))
383                 return False;
384
385         if ( !prs_uint32( "max_bytes_subkeyname", ps, depth, &nk->max_bytes_subkeyname))
386                 return False;
387         if ( !prs_uint32( "max_bytes_subkeyclassname", ps, depth, &nk->max_bytes_subkeyclassname))
388                 return False;
389         if ( !prs_uint32( "max_bytes_valuename", ps, depth, &nk->max_bytes_valuename))
390                 return False;
391         if ( !prs_uint32( "max_bytes_value", ps, depth, &nk->max_bytes_value))
392                 return False;
393         if ( !prs_uint32( "unknown index", ps, depth, &nk->unk_index))
394                 return False;
395
396         name_length = nk->keyname ? strlen(nk->keyname) : 0 ;
397         class_length = nk->classname ? strlen(nk->classname) : 0 ;
398         if ( !prs_uint16( "name_length", ps, depth, &name_length ))
399                 return False;
400         if ( !prs_uint16( "class_length", ps, depth, &class_length ))
401                 return False;   
402                 
403         if ( class_length ) {
404                 ;;
405         }
406         
407         if ( name_length ) {
408                 if ( UNMARSHALLING(ps) ) {
409                         if ( !(nk->keyname = PRS_ALLOC_MEM( ps, char, name_length+1 )) )
410                                 return False;
411                 }
412
413                 if ( !prs_uint8s( True, "name", ps, depth, (uint8*)nk->keyname, name_length) )
414                         return False;
415
416                 if ( UNMARSHALLING(ps) ) 
417                         nk->keyname[name_length] = '\0';
418         }
419
420         end_off = prs_offset( ps );
421
422         /* data_size must be divisible by 8 and large enough to hold the original record */
423
424         data_size = ((start_off - end_off) & 0xfffffff8 );
425         if ( data_size > nk->rec_size )
426                 DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, nk->rec_size));
427
428         if ( MARSHALLING(ps) )
429                 nk->hbin->dirty = True;
430
431         return True;
432 }
433
434 /*******************************************************************
435 *******************************************************************/
436
437 static uint32 regf_block_checksum( prs_struct *ps )
438 {
439         char *buffer = prs_data_p( ps );
440         uint32 checksum, x;
441         int i;
442
443         /* XOR of all bytes 0x0000 - 0x01FB */
444                 
445         checksum = x = 0;
446         
447         for ( i=0; i<0x01FB; i+=4 ) {
448                 x = IVAL(buffer, i );
449                 checksum ^= x;
450         }
451         
452         return checksum;
453 }
454
455 /*******************************************************************
456 *******************************************************************/
457
458 static bool read_regf_block( REGF_FILE *file )
459 {
460         prs_struct ps;
461         uint32 checksum;
462         
463         /* grab the first block from the file */
464                 
465         if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) == -1 )
466                 return False;
467         
468         /* parse the block and verify the checksum */
469         
470         if ( !prs_regf_block( "regf_header", &ps, 0, file ) )
471                 return False;   
472                 
473         checksum = regf_block_checksum( &ps );
474         
475         prs_mem_free( &ps );
476         
477         if ( file->checksum !=  checksum ) {
478                 DEBUG(0,("read_regf_block: invalid checksum\n" ));
479                 return False;
480         }
481
482         return True;
483 }
484
485 /*******************************************************************
486 *******************************************************************/
487
488 static REGF_HBIN* read_hbin_block( REGF_FILE *file, off_t offset )
489 {
490         REGF_HBIN *hbin;
491         uint32 record_size, curr_off, block_size, header;
492         
493         if ( !(hbin = TALLOC_ZERO_P(file->mem_ctx, REGF_HBIN)) ) 
494                 return NULL;
495         hbin->file_off = offset;
496         hbin->free_off = -1;
497                 
498         if ( read_block( file, &hbin->ps, offset, 0 ) == -1 )
499                 return NULL;
500         
501         if ( !prs_hbin_block( "hbin", &hbin->ps, 0, hbin ) )
502                 return NULL;    
503
504         /* this should be the same thing as hbin->block_size but just in case */
505
506         block_size = prs_data_size( &hbin->ps );        
507
508         /* Find the available free space offset.  Always at the end,
509            so walk the record list and stop when you get to the end.
510            The end is defined by a record header of 0xffffffff.  The 
511            previous 4 bytes contains the amount of free space remaining 
512            in the hbin block. */
513
514         /* remember that the record_size is in the 4 bytes preceeding the record itself */
515
516         if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE-sizeof(uint32) ) )
517                 return False;
518
519         record_size = 0;
520         header = 0;
521         curr_off = prs_offset( &hbin->ps );
522         while ( header != 0xffffffff ) {
523                 /* not done yet so reset the current offset to the 
524                    next record_size field */
525
526                 curr_off = curr_off+record_size;
527
528                 /* for some reason the record_size of the last record in
529                    an hbin block can extend past the end of the block
530                    even though the record fits within the remaining 
531                    space....aaarrrgggghhhhhh */
532
533                 if ( curr_off >= block_size ) {
534                         record_size = -1;
535                         curr_off = -1;
536                         break;
537                 }
538
539                 if ( !prs_set_offset( &hbin->ps, curr_off) )
540                         return False;
541
542                 if ( !prs_uint32( "rec_size", &hbin->ps, 0, &record_size ) )
543                         return False;
544                 if ( !prs_uint32( "header", &hbin->ps, 0, &header ) )
545                         return False;
546                 
547                 SMB_ASSERT( record_size != 0 );
548
549                 if ( record_size & 0x80000000 ) {
550                         /* absolute_value(record_size) */
551                         record_size = (record_size ^ 0xffffffff) + 1;
552                 }
553         }
554
555         /* save the free space offset */
556
557         if ( header == 0xffffffff ) {
558
559                 /* account for the fact that the curr_off is 4 bytes behind the actual 
560                    record header */
561
562                 hbin->free_off = curr_off + sizeof(uint32);
563                 hbin->free_size = record_size;
564         }
565
566         DEBUG(10,("read_hbin_block: free space offset == 0x%x\n", hbin->free_off));
567
568         if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE )  )
569                 return False;
570         
571         return hbin;
572 }
573
574 /*******************************************************************
575  Input a random offset and receive the corresponding HBIN 
576  block for it
577 *******************************************************************/
578
579 static bool hbin_contains_offset( REGF_HBIN *hbin, uint32 offset )
580 {
581         if ( !hbin )
582                 return False;
583         
584         if ( (offset > hbin->first_hbin_off) && (offset < (hbin->first_hbin_off+hbin->block_size)) )
585                 return True;
586                 
587         return False;
588 }
589
590 /*******************************************************************
591  Input a random offset and receive the corresponding HBIN 
592  block for it
593 *******************************************************************/
594
595 static REGF_HBIN* lookup_hbin_block( REGF_FILE *file, uint32 offset )
596 {
597         REGF_HBIN *hbin = NULL;
598         uint32 block_off;
599
600         /* start with the open list */
601
602         for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
603                 DEBUG(10,("lookup_hbin_block: address = 0x%x [0x%lx]\n", hbin->file_off, (unsigned long)hbin ));
604                 if ( hbin_contains_offset( hbin, offset ) )
605                         return hbin;
606         }
607         
608         if ( !hbin ) {
609                 /* start at the beginning */
610
611                 block_off = REGF_BLOCKSIZE;
612                 do {
613                         /* cleanup before the next round */
614                         if ( hbin )
615                                 prs_mem_free( &hbin->ps );
616
617                         hbin = read_hbin_block( file, block_off );
618
619                         if ( hbin ) 
620                                 block_off = hbin->file_off + hbin->block_size;
621
622                 } while ( hbin && !hbin_contains_offset( hbin, offset ) );
623         }
624
625         if ( hbin )
626                 DLIST_ADD( file->block_list, hbin );
627
628         return hbin;
629 }
630
631 /*******************************************************************
632 *******************************************************************/
633
634 static bool prs_hash_rec( const char *desc, prs_struct *ps, int depth, REGF_HASH_REC *hash )
635 {
636         prs_debug(ps, depth, desc, "prs_hash_rec");
637         depth++;
638
639         if ( !prs_uint32( "nk_off", ps, depth, &hash->nk_off ))
640                 return False;
641         if ( !prs_uint8s( True, "keycheck", ps, depth, hash->keycheck, sizeof( hash->keycheck )) )
642                 return False;
643         
644         return True;
645 }
646
647 /*******************************************************************
648 *******************************************************************/
649
650 static bool hbin_prs_lf_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk )
651 {
652         int i;
653         REGF_LF_REC *lf = &nk->subkeys;
654         uint32 data_size, start_off, end_off;
655
656         prs_debug(&hbin->ps, depth, desc, "prs_lf_records");
657         depth++;
658
659         /* check if we have anything to do first */
660         
661         if ( nk->num_subkeys == 0 )
662                 return True;
663
664         /* move to the LF record */
665
666         if ( !prs_set_offset( &hbin->ps, nk->subkeys_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
667                 return False;
668
669         /* backup and get the data_size */
670         
671         if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32)) )
672                 return False;
673         start_off = prs_offset( &hbin->ps );
674         if ( !prs_uint32( "rec_size", &hbin->ps, depth, &lf->rec_size ))
675                 return False;
676
677         if ( !prs_uint8s( True, "header", &hbin->ps, depth, (uint8*)lf->header, sizeof( lf->header )) )
678                 return False;
679                 
680         if ( !prs_uint16( "num_keys", &hbin->ps, depth, &lf->num_keys))
681                 return False;
682
683         if ( UNMARSHALLING(&hbin->ps) ) {
684                 if (lf->num_keys) {
685                         if ( !(lf->hashes = PRS_ALLOC_MEM( &hbin->ps, REGF_HASH_REC, lf->num_keys )) )
686                                 return False;
687                 } else {
688                         lf->hashes = NULL;
689                 }
690         }
691
692         for ( i=0; i<lf->num_keys; i++ ) {
693                 if ( !prs_hash_rec( "hash_rec", &hbin->ps, depth, &lf->hashes[i] ) )
694                         return False;
695         }
696
697         end_off = prs_offset( &hbin->ps );
698
699         /* data_size must be divisible by 8 and large enough to hold the original record */
700
701         data_size = ((start_off - end_off) & 0xfffffff8 );
702         if ( data_size > lf->rec_size )
703                 DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, lf->rec_size));
704
705         if ( MARSHALLING(&hbin->ps) )
706                 hbin->dirty = True;
707
708         return True;
709 }
710
711 /*******************************************************************
712 *******************************************************************/
713
714 static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk )
715 {
716         prs_struct *ps = &hbin->ps;
717         uint16 tag = 0xFFFF;
718         uint32 data_size, start_off, end_off;
719
720
721         prs_debug(ps, depth, desc, "hbin_prs_sk_rec");
722         depth++;
723
724         if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
725                 return False;
726
727         /* backup and get the data_size */
728         
729         if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32)) )
730                 return False;
731         start_off = prs_offset( &hbin->ps );
732         if ( !prs_uint32( "rec_size", &hbin->ps, depth, &sk->rec_size ))
733                 return False;
734
735         if ( !prs_uint8s( True, "header", ps, depth, (uint8*)sk->header, sizeof( sk->header )) )
736                 return False;
737         if ( !prs_uint16( "tag", ps, depth, &tag))
738                 return False;
739
740         if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off))
741                 return False;
742         if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off))
743                 return False;
744         if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count))
745                 return False;
746         if ( !prs_uint32( "size", ps, depth, &sk->size))
747                 return False;
748
749         {
750                 NTSTATUS status;
751                 TALLOC_CTX *mem_ctx = prs_get_mem_context(&hbin->ps);
752                 DATA_BLOB blob;
753
754                 if (MARSHALLING(&hbin->ps)) {
755                         status = marshall_sec_desc(mem_ctx,
756                                                    sk->sec_desc,
757                                                    &blob.data, &blob.length);
758                         if (!NT_STATUS_IS_OK(status))
759                                 return False;
760                         if (!prs_copy_data_in(&hbin->ps, (const char *)blob.data, blob.length))
761                                 return False;
762                 } else {
763                         blob = data_blob_const(prs_data_p(&hbin->ps),
764                                                prs_data_size(&hbin->ps));
765                         status = unmarshall_sec_desc(mem_ctx,
766                                                      blob.data, blob.length,
767                                                      &sk->sec_desc);
768                         if (!NT_STATUS_IS_OK(status))
769                                 return False;
770                         prs_set_offset(&hbin->ps, blob.length);
771                 }
772         }
773
774         end_off = prs_offset( &hbin->ps );
775
776         /* data_size must be divisible by 8 and large enough to hold the original record */
777
778         data_size = ((start_off - end_off) & 0xfffffff8 );
779         if ( data_size > sk->rec_size )
780                 DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->rec_size));
781
782         if ( MARSHALLING(&hbin->ps) )
783                 hbin->dirty = True;
784
785         return True;
786 }
787
788 /*******************************************************************
789 *******************************************************************/
790
791 static bool hbin_prs_vk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_VK_REC *vk, REGF_FILE *file )
792 {
793         uint32 offset;
794         uint16 name_length;
795         prs_struct *ps = &hbin->ps;
796         uint32 data_size, start_off, end_off;
797
798         prs_debug(ps, depth, desc, "prs_vk_rec");
799         depth++;
800
801         /* backup and get the data_size */
802         
803         if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32)) )
804                 return False;
805         start_off = prs_offset( &hbin->ps );
806         if ( !prs_uint32( "rec_size", &hbin->ps, depth, &vk->rec_size ))
807                 return False;
808
809         if ( !prs_uint8s( True, "header", ps, depth, (uint8*)vk->header, sizeof( vk->header )) )
810                 return False;
811
812         if ( MARSHALLING(&hbin->ps) )
813                 name_length = strlen(vk->valuename);
814
815         if ( !prs_uint16( "name_length", ps, depth, &name_length ))
816                 return False;
817         if ( !prs_uint32( "data_size", ps, depth, &vk->data_size ))
818                 return False;
819         if ( !prs_uint32( "data_off", ps, depth, &vk->data_off ))
820                 return False;
821         if ( !prs_uint32( "type", ps, depth, &vk->type))
822                 return False;
823         if ( !prs_uint16( "flag", ps, depth, &vk->flag))
824                 return False;
825
826         offset = prs_offset( ps );
827         offset += 2;    /* skip 2 bytes */
828         prs_set_offset( ps, offset );
829
830         /* get the name */
831
832         if ( vk->flag&VK_FLAG_NAME_PRESENT ) {
833
834                 if ( UNMARSHALLING(&hbin->ps) ) {
835                         if ( !(vk->valuename = PRS_ALLOC_MEM( ps, char, name_length+1 )))
836                                 return False;
837                 }
838                 if ( !prs_uint8s( True, "name", ps, depth, (uint8*)vk->valuename, name_length ) )
839                         return False;
840         }
841
842         end_off = prs_offset( &hbin->ps );
843
844         /* get the data if necessary */
845
846         if ( vk->data_size != 0 ) {
847                 bool charmode = False;
848
849                 if ( (vk->type == REG_SZ) || (vk->type == REG_MULTI_SZ) )
850                         charmode = True;
851
852                 /* the data is stored in the offset if the size <= 4 */
853
854                 if ( !(vk->data_size & VK_DATA_IN_OFFSET) ) {
855                         REGF_HBIN *hblock = hbin;
856                         uint32 data_rec_size;
857
858                         if ( UNMARSHALLING(&hbin->ps) ) {
859                                 if ( !(vk->data = PRS_ALLOC_MEM( ps, uint8, vk->data_size) ) )
860                                         return False;
861                         }
862
863                         /* this data can be in another hbin */
864                         if ( !hbin_contains_offset( hbin, vk->data_off ) ) {
865                                 if ( !(hblock = lookup_hbin_block( file, vk->data_off )) )
866                                         return False;
867                         }
868                         if ( !(prs_set_offset( &hblock->ps, (vk->data_off+HBIN_HDR_SIZE-hblock->first_hbin_off)-sizeof(uint32) )) )
869                                 return False;
870
871                         if ( MARSHALLING(&hblock->ps) ) {
872                                 data_rec_size = ( (vk->data_size+sizeof(uint32)) & 0xfffffff8 ) + 8;
873                                 data_rec_size = ( data_rec_size - 1 ) ^ 0xFFFFFFFF;
874                         }
875                         if ( !prs_uint32( "data_rec_size", &hblock->ps, depth, &data_rec_size ))
876                                 return False;
877                         if ( !prs_uint8s( charmode, "data", &hblock->ps, depth, vk->data, vk->data_size) )
878                                 return False;
879
880                         if ( MARSHALLING(&hblock->ps) )
881                                 hblock->dirty = True;
882                 }
883                 else {
884                         if ( !(vk->data = PRS_ALLOC_MEM( ps, uint8, 4 ) ) )
885                                 return False;
886                         SIVAL( vk->data, 0, vk->data_off );
887                 }
888                 
889         }
890
891         /* data_size must be divisible by 8 and large enough to hold the original record */
892
893         data_size = ((start_off - end_off ) & 0xfffffff8 );
894         if ( data_size !=  vk->rec_size )
895                 DEBUG(10,("prs_vk_rec: data_size check failed (0x%x < 0x%x)\n", data_size, vk->rec_size));
896
897         if ( MARSHALLING(&hbin->ps) )
898                 hbin->dirty = True;
899
900         return True;
901 }
902
903 /*******************************************************************
904  read a VK record which is contained in the HBIN block stored 
905  in the prs_struct *ps.
906 *******************************************************************/
907
908 static bool hbin_prs_vk_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk, REGF_FILE *file )
909 {
910         int i;
911         uint32 record_size;
912
913         prs_debug(&hbin->ps, depth, desc, "prs_vk_records");
914         depth++;
915         
916         /* check if we have anything to do first */
917         
918         if ( nk->num_values == 0 )
919                 return True;
920                 
921         if ( UNMARSHALLING(&hbin->ps) ) {
922                 if ( !(nk->values = PRS_ALLOC_MEM( &hbin->ps, REGF_VK_REC, nk->num_values ) ) )
923                         return False;
924         }
925         
926         /* convert the offset to something relative to this HBIN block */
927         
928         if ( !prs_set_offset( &hbin->ps, nk->values_off+HBIN_HDR_SIZE-hbin->first_hbin_off-sizeof(uint32)) )
929                 return False;
930
931         if ( MARSHALLING( &hbin->ps) ) { 
932                 record_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8;
933                 record_size = (record_size - 1) ^ 0xFFFFFFFF;
934         }
935
936         if ( !prs_uint32( "record_size", &hbin->ps, depth, &record_size ) )
937                 return False;
938                 
939         for ( i=0; i<nk->num_values; i++ ) {
940                 if ( !prs_uint32( "vk_off", &hbin->ps, depth, &nk->values[i].rec_off ) )
941                         return False;
942         }
943
944         for ( i=0; i<nk->num_values; i++ ) {
945                 REGF_HBIN *sub_hbin = hbin;
946                 uint32 new_offset;
947         
948                 if ( !hbin_contains_offset( hbin, nk->values[i].rec_off ) ) {
949                         sub_hbin = lookup_hbin_block( file, nk->values[i].rec_off );
950                         if ( !sub_hbin ) {
951                                 DEBUG(0,("hbin_prs_vk_records: Failed to find HBIN block containing offset [0x%x]\n", 
952                                         nk->values[i].hbin_off));
953                                 return False;
954                         }
955                 }
956                 
957                 new_offset = nk->values[i].rec_off + HBIN_HDR_SIZE - sub_hbin->first_hbin_off;
958                 if ( !prs_set_offset( &sub_hbin->ps, new_offset ) )
959                         return False;
960                 if ( !hbin_prs_vk_rec( "vk_rec", sub_hbin, depth, &nk->values[i], file ) )
961                         return False;
962         }
963
964         if ( MARSHALLING(&hbin->ps) )
965                 hbin->dirty = True;
966
967
968         return True;
969 }
970
971
972 /*******************************************************************
973 *******************************************************************/
974
975 static REGF_SK_REC* find_sk_record_by_offset( REGF_FILE *file, uint32 offset )
976 {
977         REGF_SK_REC *p_sk;
978         
979         for ( p_sk=file->sec_desc_list; p_sk; p_sk=p_sk->next ) {
980                 if ( p_sk->sk_off == offset ) 
981                         return p_sk;
982         }
983         
984         return NULL;
985 }
986
987 /*******************************************************************
988 *******************************************************************/
989
990 static REGF_SK_REC* find_sk_record_by_sec_desc( REGF_FILE *file, struct security_descriptor *sd )
991 {
992         REGF_SK_REC *p;
993
994         for ( p=file->sec_desc_list; p; p=p->next ) {
995                 if ( security_descriptor_equal( p->sec_desc, sd ) )
996                         return p;
997         }
998
999         /* failure */
1000
1001         return NULL;
1002 }
1003
1004 /*******************************************************************
1005 *******************************************************************/
1006
1007 static bool hbin_prs_key( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk )
1008 {
1009         int depth = 0;
1010         REGF_HBIN *sub_hbin;
1011         
1012         prs_debug(&hbin->ps, depth, "", "fetch_key");
1013         depth++;
1014
1015         /* get the initial nk record */
1016         
1017         if ( !prs_nk_rec( "nk_rec", &hbin->ps, depth, nk ))
1018                 return False;
1019
1020         /* fill in values */
1021         
1022         if ( nk->num_values && (nk->values_off!=REGF_OFFSET_NONE) ) {
1023                 sub_hbin = hbin;
1024                 if ( !hbin_contains_offset( hbin, nk->values_off ) ) {
1025                         sub_hbin = lookup_hbin_block( file, nk->values_off );
1026                         if ( !sub_hbin ) {
1027                                 DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing value_list_offset [0x%x]\n", 
1028                                         nk->values_off));
1029                                 return False;
1030                         }
1031                 }
1032                 
1033                 if ( !hbin_prs_vk_records( "vk_rec", sub_hbin, depth, nk, file ))
1034                         return False;
1035         }
1036                 
1037         /* now get subkeys */
1038         
1039         if ( nk->num_subkeys && (nk->subkeys_off!=REGF_OFFSET_NONE) ) {
1040                 sub_hbin = hbin;
1041                 if ( !hbin_contains_offset( hbin, nk->subkeys_off ) ) {
1042                         sub_hbin = lookup_hbin_block( file, nk->subkeys_off );
1043                         if ( !sub_hbin ) {
1044                                 DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing subkey_offset [0x%x]\n", 
1045                                         nk->subkeys_off));
1046                                 return False;
1047                         }
1048                 }
1049                 
1050                 if ( !hbin_prs_lf_records( "lf_rec", sub_hbin, depth, nk ))
1051                         return False;
1052         }
1053
1054         /* get the to the security descriptor.  First look if we have already parsed it */
1055         
1056         if ( (nk->sk_off!=REGF_OFFSET_NONE) && !( nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )) ) {
1057
1058                 sub_hbin = hbin;
1059                 if ( !hbin_contains_offset( hbin, nk->sk_off ) ) {
1060                         sub_hbin = lookup_hbin_block( file, nk->sk_off );
1061                         if ( !sub_hbin ) {
1062                                 DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing sk_offset [0x%x]\n", 
1063                                         nk->subkeys_off));
1064                                 return False;
1065                         }
1066                 }
1067                 
1068                 if ( !(nk->sec_desc = TALLOC_ZERO_P( file->mem_ctx, REGF_SK_REC )) )
1069                         return False;
1070                 nk->sec_desc->sk_off = nk->sk_off;
1071                 if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc ))
1072                         return False;
1073                         
1074                 /* add to the list of security descriptors (ref_count has been read from the files) */
1075
1076                 nk->sec_desc->sk_off = nk->sk_off;
1077                 DLIST_ADD( file->sec_desc_list, nk->sec_desc );
1078         }
1079                 
1080         return True;
1081 }
1082
1083 /*******************************************************************
1084 *******************************************************************/
1085
1086 static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob )
1087 {
1088         uint8 header[REC_HDR_SIZE];
1089         uint32 record_size;
1090         uint32 curr_off, block_size;
1091         bool found = False;
1092         prs_struct *ps = &hbin->ps;
1093         
1094         curr_off = prs_offset( ps );
1095         if ( curr_off == 0 )
1096                 prs_set_offset( ps, HBIN_HEADER_REC_SIZE );
1097
1098         /* assume that the current offset is at the record header 
1099            and we need to backup to read the record size */
1100
1101         curr_off -= sizeof(uint32);
1102
1103         block_size = prs_data_size( ps );
1104         record_size = 0;
1105         memset( header, 0x0, sizeof(uint8)*REC_HDR_SIZE );
1106         while ( !found ) {
1107
1108                 curr_off = curr_off+record_size;
1109                 if ( curr_off >= block_size ) 
1110                         break;
1111
1112                 if ( !prs_set_offset( &hbin->ps, curr_off) )
1113                         return False;
1114
1115                 if ( !prs_uint32( "record_size", ps, 0, &record_size ) )
1116                         return False;
1117                 if ( !prs_uint8s( True, "header", ps, 0, header, REC_HDR_SIZE ) )
1118                         return False;
1119
1120                 if ( record_size & 0x80000000 ) {
1121                         /* absolute_value(record_size) */
1122                         record_size = (record_size ^ 0xffffffff) + 1;
1123                 }
1124
1125                 if ( memcmp( header, hdr, REC_HDR_SIZE ) == 0 ) {
1126                         found = True;
1127                         curr_off += sizeof(uint32);
1128                 }
1129         } 
1130
1131         /* mark prs_struct as done ( at end ) if no more SK records */
1132         /* mark end-of-block as True */
1133         
1134         if ( !found ) {
1135                 prs_set_offset( &hbin->ps, prs_data_size(&hbin->ps) );
1136                 *eob = True;
1137                 return False;
1138         }
1139                 
1140         if ( !prs_set_offset( ps, curr_off ) )
1141                 return False;
1142
1143         return True;
1144 }
1145
1146 /*******************************************************************
1147 *******************************************************************/
1148
1149 static bool next_nk_record( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk, bool *eob )
1150 {
1151         if ( next_record( hbin, "nk", eob ) && hbin_prs_key( file, hbin, nk ) )
1152                 return True;
1153         
1154         return False;
1155 }
1156
1157 /*******************************************************************
1158  Intialize the newly created REGF_BLOCK in *file and write the 
1159  block header to disk 
1160 *******************************************************************/
1161
1162 static bool init_regf_block( REGF_FILE *file )
1163 {       
1164         prs_struct ps;
1165         bool result = True;
1166         
1167         if ( !prs_init( &ps, REGF_BLOCKSIZE, file->mem_ctx, MARSHALL ) )
1168                 return False;
1169                 
1170         memcpy( file->header, "regf", REGF_HDR_SIZE );
1171         file->data_offset = 0x20;
1172         file->last_block  = 0x1000;
1173         
1174         /* set mod time */
1175         
1176         unix_to_nt_time( &file->mtime, time(NULL) );
1177         
1178         /* hard coded values...no diea what these are ... maybe in time */
1179         
1180         file->unknown1 = 0x2;
1181         file->unknown2 = 0x1;
1182         file->unknown3 = 0x3;
1183         file->unknown4 = 0x0;
1184         file->unknown5 = 0x1;
1185         file->unknown6 = 0x1;
1186         
1187         /* write header to the buffer */
1188         
1189         if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) {
1190                 result = False;
1191                 goto out;
1192         }
1193         
1194         /* calculate the checksum, re-marshall data (to include the checksum) 
1195            and write to disk */
1196         
1197         file->checksum = regf_block_checksum( &ps );
1198         prs_set_offset( &ps, 0 );
1199         if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) {
1200                 result = False;
1201                 goto out;
1202         }
1203                 
1204         if ( write_block( file, &ps, 0 ) == -1 ) {
1205                 DEBUG(0,("init_regf_block: Failed to initialize registry header block!\n"));
1206                 result = False;
1207                 goto out;
1208         }
1209         
1210 out:
1211         prs_mem_free( &ps );
1212
1213         return result;
1214 }
1215 /*******************************************************************
1216  Open the registry file and then read in the REGF block to get the 
1217  first hbin offset.
1218 *******************************************************************/
1219
1220  REGF_FILE* regfio_open( const char *filename, int flags, int mode )
1221 {
1222         REGF_FILE *rb;
1223         
1224         if ( !(rb = SMB_MALLOC_P(REGF_FILE)) ) {
1225                 DEBUG(0,("ERROR allocating memory\n"));
1226                 return NULL;
1227         }
1228         ZERO_STRUCTP( rb );
1229         rb->fd = -1;
1230         
1231         if ( !(rb->mem_ctx = talloc_init( "read_regf_block" )) ) {
1232                 regfio_close( rb );
1233                 return NULL;
1234         }
1235
1236         rb->open_flags = flags;
1237         
1238         /* open and existing file */
1239
1240         if ( (rb->fd = open(filename, flags, mode)) == -1 ) {
1241                 DEBUG(0,("regfio_open: failure to open %s (%s)\n", filename, strerror(errno)));
1242                 regfio_close( rb );
1243                 return NULL;
1244         }
1245         
1246         /* check if we are creating a new file or overwriting an existing one */
1247                 
1248         if ( flags & (O_CREAT|O_TRUNC) ) {
1249                 if ( !init_regf_block( rb ) ) {
1250                         DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));
1251                         regfio_close( rb );
1252                         return NULL;
1253                 }
1254                 
1255                 /* success */
1256                 return rb;
1257         }
1258         
1259         /* read in an existing file */
1260         
1261         if ( !read_regf_block( rb ) ) {
1262                 DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));
1263                 regfio_close( rb );
1264                 return NULL;
1265         }
1266         
1267         /* success */
1268         
1269         return rb;
1270 }
1271
1272 /*******************************************************************
1273 *******************************************************************/
1274
1275 static void regfio_mem_free( REGF_FILE *file )
1276 {
1277         /* free any talloc()'d memory */
1278         
1279         if ( file && file->mem_ctx )
1280                 talloc_destroy( file->mem_ctx );        
1281 }
1282
1283 /*******************************************************************
1284 *******************************************************************/
1285
1286  int regfio_close( REGF_FILE *file )
1287 {
1288         int fd;
1289
1290         /* cleanup for a file opened for write */
1291
1292         if ((file->fd != -1) && (file->open_flags & (O_WRONLY|O_RDWR))) {
1293                 prs_struct ps;
1294                 REGF_SK_REC *sk;
1295
1296                 /* write of sd list */
1297
1298                 for ( sk=file->sec_desc_list; sk; sk=sk->next ) {
1299                         hbin_prs_sk_rec( "sk_rec", sk->hbin, 0, sk );
1300                 }
1301
1302                 /* flush any dirty blocks */
1303
1304                 while ( file->block_list ) {
1305                         hbin_block_close( file, file->block_list );
1306                 } 
1307
1308                 ZERO_STRUCT( ps );
1309
1310                 unix_to_nt_time( &file->mtime, time(NULL) );
1311
1312                 if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) != -1 ) {
1313                         /* now use for writing */
1314                         prs_switch_type( &ps, MARSHALL );
1315
1316                         /* stream the block once, generate the checksum, 
1317                            and stream it again */
1318                         prs_set_offset( &ps, 0 );
1319                         prs_regf_block( "regf_blocK", &ps, 0, file );
1320                         file->checksum = regf_block_checksum( &ps );
1321                         prs_set_offset( &ps, 0 );
1322                         prs_regf_block( "regf_blocK", &ps, 0, file );
1323
1324                         /* now we are ready to write it to disk */
1325                         if ( write_block( file, &ps, 0 ) == -1 )
1326                                 DEBUG(0,("regfio_close: failed to update the regf header block!\n"));
1327                 }
1328
1329                 prs_mem_free( &ps );
1330         }
1331         
1332         regfio_mem_free( file );
1333
1334         /* nothing tdo do if there is no open file */
1335
1336         if (file->fd == -1)
1337                 return 0;
1338                 
1339         fd = file->fd;
1340         file->fd = -1;
1341         SAFE_FREE( file );
1342
1343         return close( fd );
1344 }
1345
1346 /*******************************************************************
1347 *******************************************************************/
1348
1349 static void regfio_flush( REGF_FILE *file )
1350 {
1351         REGF_HBIN *hbin;
1352
1353         for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
1354                 write_hbin_block( file, hbin );
1355         }
1356 }
1357
1358 /*******************************************************************
1359  There should be only *one* root key in the registry file based 
1360  on my experience.  --jerry
1361 *******************************************************************/
1362
1363 REGF_NK_REC* regfio_rootkey( REGF_FILE *file )
1364 {
1365         REGF_NK_REC *nk;
1366         REGF_HBIN   *hbin;
1367         uint32      offset = REGF_BLOCKSIZE;
1368         bool        found = False;
1369         bool        eob;
1370         
1371         if ( !file )
1372                 return NULL;
1373                 
1374         if ( !(nk = TALLOC_ZERO_P( file->mem_ctx, REGF_NK_REC )) ) {
1375                 DEBUG(0,("regfio_rootkey: talloc() failed!\n"));
1376                 return NULL;
1377         }
1378         
1379         /* scan through the file on HBIN block at a time looking 
1380            for an NK record with a type == 0x002c.
1381            Normally this is the first nk record in the first hbin 
1382            block (but I'm not assuming that for now) */
1383         
1384         while ( (hbin = read_hbin_block( file, offset )) ) {
1385                 eob = False;
1386
1387                 while ( !eob) {
1388                         if ( next_nk_record( file, hbin, nk, &eob ) ) {
1389                                 if ( nk->key_type == NK_TYPE_ROOTKEY ) {
1390                                         found = True;
1391                                         break;
1392                                 }
1393                         }
1394                         prs_mem_free( &hbin->ps );
1395                 }
1396                 
1397                 if ( found ) 
1398                         break;
1399
1400                 offset += hbin->block_size;
1401         }
1402         
1403         if ( !found ) {
1404                 DEBUG(0,("regfio_rootkey: corrupt registry file ?  No root key record located\n"));
1405                 return NULL;
1406         }
1407
1408         DLIST_ADD( file->block_list, hbin );
1409
1410         return nk;              
1411 }
1412
1413 /*******************************************************************
1414  This acts as an interator over the subkeys defined for a given 
1415  NK record.  Remember that offsets are from the *first* HBIN block.
1416 *******************************************************************/
1417
1418  REGF_NK_REC* regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk )
1419 {
1420         REGF_NK_REC *subkey;
1421         REGF_HBIN   *hbin;
1422         uint32      nk_offset;
1423
1424         /* see if there is anything left to report */
1425         
1426         if ( !nk || (nk->subkeys_off==REGF_OFFSET_NONE) || (nk->subkey_index >= nk->num_subkeys) )
1427                 return NULL;
1428
1429         /* find the HBIN block which should contain the nk record */
1430         
1431         if ( !(hbin = lookup_hbin_block( file, nk->subkeys.hashes[nk->subkey_index].nk_off )) ) {
1432                 DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing offset [0x%x]\n", 
1433                         nk->subkeys.hashes[nk->subkey_index].nk_off));
1434                 return NULL;
1435         }
1436         
1437         nk_offset = nk->subkeys.hashes[nk->subkey_index].nk_off;
1438         if ( !prs_set_offset( &hbin->ps, (HBIN_HDR_SIZE + nk_offset - hbin->first_hbin_off) ) )
1439                 return NULL;
1440                 
1441         nk->subkey_index++;
1442         if ( !(subkey = TALLOC_ZERO_P( file->mem_ctx, REGF_NK_REC )) )
1443                 return NULL;
1444                 
1445         if ( !hbin_prs_key( file, hbin, subkey ) )
1446                 return NULL;
1447         
1448         return subkey;
1449 }
1450
1451
1452 /*******************************************************************
1453 *******************************************************************/
1454
1455 static REGF_HBIN* regf_hbin_allocate( REGF_FILE *file, uint32 block_size )
1456 {
1457         REGF_HBIN *hbin;
1458         SMB_STRUCT_STAT sbuf;
1459
1460         if ( !(hbin = TALLOC_ZERO_P( file->mem_ctx, REGF_HBIN )) )
1461                 return NULL;
1462
1463         memcpy( hbin->header, "hbin", sizeof(HBIN_HDR_SIZE) );
1464
1465
1466         if (sys_fstat(file->fd, &sbuf, false)) {
1467                 DEBUG(0,("regf_hbin_allocate: stat() failed! (%s)\n", strerror(errno)));
1468                 return NULL;
1469         }
1470
1471         hbin->file_off       = sbuf.st_ex_size;
1472
1473         hbin->free_off       = HBIN_HEADER_REC_SIZE;
1474         hbin->free_size      = block_size - hbin->free_off + sizeof(uint32);;
1475
1476         hbin->block_size     = block_size;
1477         hbin->first_hbin_off = hbin->file_off - REGF_BLOCKSIZE;
1478
1479         if ( !prs_init( &hbin->ps, block_size, file->mem_ctx, MARSHALL ) )
1480                 return NULL;
1481
1482         if ( !prs_hbin_block( "new_hbin", &hbin->ps, 0, hbin ) )
1483                 return NULL;
1484
1485         if ( !write_hbin_block( file, hbin ) )
1486                 return NULL;
1487
1488         file->last_block = hbin->file_off;
1489
1490         return hbin;
1491 }
1492
1493 /*******************************************************************
1494 *******************************************************************/
1495
1496 static void update_free_space( REGF_HBIN *hbin, uint32 size_used )
1497 {
1498         hbin->free_off  += size_used;
1499         hbin->free_size -= size_used;
1500
1501         if ( hbin->free_off >= hbin->block_size ) {
1502                 hbin->free_off = REGF_OFFSET_NONE;
1503         }
1504
1505         return;
1506 }
1507
1508 /*******************************************************************
1509 *******************************************************************/
1510
1511 static REGF_HBIN* find_free_space( REGF_FILE *file, uint32 size )
1512 {
1513         REGF_HBIN *hbin, *p_hbin;
1514         uint32 block_off;
1515         bool cached;
1516
1517         /* check open block list */
1518
1519         for ( hbin=file->block_list; hbin!=NULL; hbin=hbin->next ) {
1520                 /* only check blocks that actually have available space */
1521
1522                 if ( hbin->free_off == REGF_OFFSET_NONE )
1523                         continue;
1524
1525                 /* check for a large enough available chunk */
1526
1527                 if ( (hbin->block_size - hbin->free_off) >= size ) {
1528                         DLIST_PROMOTE( file->block_list, hbin );
1529                         goto done;                      
1530                 }
1531         }
1532
1533         /* parse the file until we find a block with 
1534            enough free space; save the last non-filled hbin */
1535
1536         block_off = REGF_BLOCKSIZE;
1537         do {
1538                 /* cleanup before the next round */
1539                 cached = False;
1540                 if ( hbin )
1541                         prs_mem_free( &hbin->ps );
1542
1543                 hbin = read_hbin_block( file, block_off );
1544
1545                 if ( hbin ) {
1546
1547                         /* make sure that we don't already have this block in memory */
1548
1549                         for ( p_hbin=file->block_list; p_hbin!=NULL; p_hbin=p_hbin->next ) {
1550                                 if ( p_hbin->file_off == hbin->file_off ) {
1551                                         cached = True;  
1552                                         break;
1553                                 }
1554                         }
1555
1556                         block_off = hbin->file_off + hbin->block_size;
1557
1558                         if ( cached ) {
1559                                 prs_mem_free( &hbin->ps );
1560                                 hbin = NULL;
1561                                 continue;
1562                         }
1563                 }
1564         /* if (cached block or (new block and not enough free space)) then continue looping */
1565         } while ( cached || (hbin && (hbin->free_size < size)) );
1566         
1567         /* no free space; allocate a new one */
1568
1569         if ( !hbin ) {
1570                 uint32 alloc_size;
1571
1572                 /* allocate in multiples of REGF_ALLOC_BLOCK; make sure (size + hbin_header) fits */
1573
1574                 alloc_size = (((size+HBIN_HEADER_REC_SIZE) / REGF_ALLOC_BLOCK ) + 1 ) * REGF_ALLOC_BLOCK;
1575
1576                 if ( !(hbin = regf_hbin_allocate( file, alloc_size )) ) {
1577                         DEBUG(0,("find_free_space: regf_hbin_allocate() failed!\n"));
1578                         return NULL;
1579                 }
1580                 DLIST_ADD( file->block_list, hbin );
1581         }
1582
1583 done:
1584         /* set the offset to be ready to write */
1585
1586         if ( !prs_set_offset( &hbin->ps, hbin->free_off-sizeof(uint32) ) )
1587                 return NULL;
1588
1589         /* write the record size as a placeholder for now, it should be
1590            probably updated by the caller once it all of the data necessary 
1591            for the record */
1592
1593         if ( !prs_uint32("allocated_size", &hbin->ps, 0, &size) )
1594                 return False;
1595
1596         update_free_space( hbin, size );
1597         
1598         return hbin;
1599 }
1600
1601 /*******************************************************************
1602 *******************************************************************/
1603
1604 static uint32 sk_record_data_size( struct security_descriptor * sd )
1605 {
1606         uint32 size, size_mod8;
1607
1608         size_mod8 = 0;
1609
1610         /* the record size is sizeof(hdr) + name + static members + data_size_field */
1611
1612         size = sizeof(uint32)*5 + ndr_size_security_descriptor(sd, 0) + sizeof(uint32);
1613
1614         /* multiple of 8 */
1615         size_mod8 = size & 0xfffffff8;
1616         if ( size_mod8 < size )
1617                 size_mod8 += 8;
1618
1619         return size_mod8;
1620 }
1621
1622 /*******************************************************************
1623 *******************************************************************/
1624
1625 static uint32 vk_record_data_size( REGF_VK_REC *vk )
1626 {
1627         uint32 size, size_mod8;
1628
1629         size_mod8 = 0;
1630
1631         /* the record size is sizeof(hdr) + name + static members + data_size_field */
1632
1633         size = REC_HDR_SIZE + (sizeof(uint16)*3) + (sizeof(uint32)*3) + sizeof(uint32);
1634
1635         if ( vk->valuename )
1636                 size += strlen(vk->valuename);
1637
1638         /* multiple of 8 */
1639         size_mod8 = size & 0xfffffff8;
1640         if ( size_mod8 < size )
1641                 size_mod8 += 8;
1642
1643         return size_mod8;
1644 }
1645
1646 /*******************************************************************
1647 *******************************************************************/
1648
1649 static uint32 lf_record_data_size( uint32 num_keys )
1650 {
1651         uint32 size, size_mod8;
1652
1653         size_mod8 = 0;
1654
1655         /* the record size is sizeof(hdr) + num_keys + sizeof of hash_array + data_size_uint32 */
1656
1657         size = REC_HDR_SIZE + sizeof(uint16) + (sizeof(REGF_HASH_REC) * num_keys) + sizeof(uint32);
1658
1659         /* multiple of 8 */
1660         size_mod8 = size & 0xfffffff8;
1661         if ( size_mod8 < size )
1662                 size_mod8 += 8;
1663
1664         return size_mod8;
1665 }
1666
1667 /*******************************************************************
1668 *******************************************************************/
1669
1670 static uint32 nk_record_data_size( REGF_NK_REC *nk )
1671 {
1672         uint32 size, size_mod8;
1673
1674         size_mod8 = 0;
1675
1676         /* the record size is static + length_of_keyname + length_of_classname + data_size_uint32 */
1677
1678         size = 0x4c + strlen(nk->keyname) + sizeof(uint32);
1679
1680         if ( nk->classname )
1681                 size += strlen( nk->classname );
1682
1683         /* multiple of 8 */
1684         size_mod8 = size & 0xfffffff8;
1685         if ( size_mod8 < size )
1686                 size_mod8 += 8;
1687
1688         return size_mod8;
1689 }
1690
1691 /*******************************************************************
1692 *******************************************************************/
1693
1694 static bool create_vk_record(REGF_FILE *file, REGF_VK_REC *vk,
1695                              struct regval_blob *value)
1696 {
1697         char *name = regval_name(value);
1698         REGF_HBIN *data_hbin;
1699
1700         ZERO_STRUCTP( vk );
1701
1702         memcpy( vk->header, "vk", REC_HDR_SIZE );
1703
1704         if ( name ) {
1705                 vk->valuename = talloc_strdup( file->mem_ctx, regval_name(value) );
1706                 vk->flag = VK_FLAG_NAME_PRESENT;
1707         }
1708
1709         vk->data_size = regval_size( value );
1710         vk->type      = regval_type( value );
1711
1712         if ( vk->data_size > sizeof(uint32) ) {
1713                 uint32 data_size = ( (vk->data_size+sizeof(uint32)) & 0xfffffff8 ) + 8;
1714
1715                 vk->data = (uint8 *)TALLOC_MEMDUP( file->mem_ctx,
1716                                                    regval_data_p(value),
1717                                                    vk->data_size );
1718                 if (vk->data == NULL) {
1719                         return False;
1720                 }
1721
1722                 /* go ahead and store the offset....we'll pick this hbin block back up when 
1723                    we stream the data */
1724
1725                 if ((data_hbin = find_free_space(file, data_size )) == NULL) {
1726                         return False;
1727                 }
1728                 vk->data_off = prs_offset( &data_hbin->ps ) + data_hbin->first_hbin_off - HBIN_HDR_SIZE;
1729         }
1730         else {
1731                 /* make sure we don't try to copy from a NULL value pointer */
1732
1733                 if ( vk->data_size != 0 ) 
1734                         memcpy( &vk->data_off, regval_data_p(value), sizeof(uint32) );
1735                 vk->data_size |= VK_DATA_IN_OFFSET;             
1736         }
1737
1738         return True;
1739 }
1740
1741 /*******************************************************************
1742 *******************************************************************/
1743
1744 static int hashrec_cmp( REGF_HASH_REC *h1, REGF_HASH_REC *h2 )
1745 {
1746         return StrCaseCmp( h1->fullname, h2->fullname );
1747 }
1748
1749 /*******************************************************************
1750 *******************************************************************/
1751
1752  REGF_NK_REC* regfio_write_key( REGF_FILE *file, const char *name,
1753                                struct regval_ctr *values, struct regsubkey_ctr *subkeys,
1754                                struct security_descriptor *sec_desc, REGF_NK_REC *parent )
1755 {
1756         REGF_NK_REC *nk;
1757         REGF_HBIN *vlist_hbin = NULL;
1758         uint32 size;
1759
1760         if ( !(nk = TALLOC_ZERO_P( file->mem_ctx, REGF_NK_REC )) )
1761                 return NULL;
1762
1763         memcpy( nk->header, "nk", REC_HDR_SIZE );
1764
1765         if ( !parent )
1766                 nk->key_type = NK_TYPE_ROOTKEY;
1767         else
1768                 nk->key_type = NK_TYPE_NORMALKEY;
1769
1770         /* store the parent offset (or -1 if a the root key */
1771
1772         nk->parent_off = parent ? (parent->hbin_off + parent->hbin->file_off - REGF_BLOCKSIZE - HBIN_HDR_SIZE ) : REGF_OFFSET_NONE;
1773
1774         /* no classname currently */
1775
1776         nk->classname_off = REGF_OFFSET_NONE;
1777         nk->classname = NULL;
1778         nk->keyname = talloc_strdup( file->mem_ctx, name );
1779
1780         /* current modification time */
1781
1782         unix_to_nt_time( &nk->mtime, time(NULL) );
1783
1784         /* allocate the record on disk */
1785
1786         size = nk_record_data_size( nk );
1787         nk->rec_size = ( size - 1 ) ^ 0XFFFFFFFF;
1788         if ((nk->hbin = find_free_space( file, size )) == NULL) {
1789                 return NULL;
1790         }
1791         nk->hbin_off = prs_offset( &nk->hbin->ps );
1792
1793         /* Update the hash record in the parent */
1794         
1795         if ( parent ) {
1796                 REGF_HASH_REC *hash = &parent->subkeys.hashes[parent->subkey_index];
1797
1798                 hash->nk_off = prs_offset( &nk->hbin->ps ) + nk->hbin->first_hbin_off - HBIN_HDR_SIZE;
1799                 memcpy( hash->keycheck, name, sizeof(uint32) );
1800                 hash->fullname = talloc_strdup( file->mem_ctx, name );
1801                 parent->subkey_index++;
1802
1803                 /* sort the list by keyname */
1804                 TYPESAFE_QSORT(parent->subkeys.hashes, parent->subkey_index, hashrec_cmp);
1805
1806                 if ( !hbin_prs_lf_records( "lf_rec", parent->subkeys.hbin, 0, parent ) )
1807                         return False;
1808         }
1809
1810         /* write the security descriptor */
1811
1812         nk->sk_off = REGF_OFFSET_NONE;
1813         if ( sec_desc ) {
1814                 uint32 sk_size = sk_record_data_size( sec_desc );
1815                 REGF_HBIN *sk_hbin;
1816
1817                 /* search for it in the existing list of sd's */
1818
1819                 if ( (nk->sec_desc = find_sk_record_by_sec_desc( file, sec_desc )) == NULL ) {
1820                         /* not found so add it to the list */
1821
1822                         if (!(sk_hbin = find_free_space( file, sk_size ))) {
1823                                 return NULL;
1824                         }
1825
1826                         if ( !(nk->sec_desc = TALLOC_ZERO_P( file->mem_ctx, REGF_SK_REC )) )
1827                                 return NULL;
1828         
1829                         /* now we have to store the security descriptor in the list and 
1830                            update the offsets */
1831
1832                         memcpy( nk->sec_desc->header, "sk", REC_HDR_SIZE );
1833                         nk->sec_desc->hbin      = sk_hbin;
1834                         nk->sec_desc->hbin_off  = prs_offset( &sk_hbin->ps );
1835                         nk->sec_desc->sk_off    = prs_offset( &sk_hbin->ps ) + sk_hbin->first_hbin_off - HBIN_HDR_SIZE;
1836                         nk->sec_desc->rec_size  = (sk_size-1)  ^ 0xFFFFFFFF;
1837
1838                         nk->sec_desc->sec_desc  = sec_desc;
1839                         nk->sec_desc->ref_count = 0;
1840                         
1841                         /* size value must be self-inclusive */
1842                         nk->sec_desc->size      = ndr_size_security_descriptor(sec_desc, 0)
1843                                 + sizeof(uint32);
1844
1845                         DLIST_ADD_END( file->sec_desc_list, nk->sec_desc, REGF_SK_REC *);
1846
1847                         /* update the offsets for us and the previous sd in the list.
1848                            if this is the first record, then just set the next and prev
1849                            offsets to ourself. */
1850
1851                         if ( DLIST_PREV(nk->sec_desc) ) {
1852                                 REGF_SK_REC *prev = DLIST_PREV(nk->sec_desc);
1853
1854                                 nk->sec_desc->prev_sk_off = prev->hbin_off + prev->hbin->first_hbin_off - HBIN_HDR_SIZE;
1855                                 prev->next_sk_off = nk->sec_desc->sk_off;
1856
1857                                 /* the end must loop around to the front */
1858                                 nk->sec_desc->next_sk_off = file->sec_desc_list->sk_off;
1859
1860                                 /* and first must loop around to the tail */
1861                                 file->sec_desc_list->prev_sk_off = nk->sec_desc->sk_off;
1862                         } else {
1863                                 nk->sec_desc->prev_sk_off = nk->sec_desc->sk_off;
1864                                 nk->sec_desc->next_sk_off = nk->sec_desc->sk_off;
1865                         }
1866                 }
1867
1868                 /* bump the reference count +1 */
1869
1870                 nk->sk_off = nk->sec_desc->sk_off;
1871                 nk->sec_desc->ref_count++;
1872         }
1873
1874         /* write the subkeys */
1875
1876         nk->subkeys_off = REGF_OFFSET_NONE;
1877         if ( (nk->num_subkeys = regsubkey_ctr_numkeys( subkeys )) != 0 ) {
1878                 uint32 lf_size = lf_record_data_size( nk->num_subkeys );
1879                 uint32 namelen;
1880                 int i;
1881                 
1882                 if (!(nk->subkeys.hbin = find_free_space( file, lf_size ))) {
1883                         return NULL;
1884                 }
1885                 nk->subkeys.hbin_off = prs_offset( &nk->subkeys.hbin->ps );
1886                 nk->subkeys.rec_size = (lf_size-1) ^ 0xFFFFFFFF;
1887                 nk->subkeys_off = prs_offset( &nk->subkeys.hbin->ps ) + nk->subkeys.hbin->first_hbin_off - HBIN_HDR_SIZE;
1888
1889                 memcpy( nk->subkeys.header, "lf", REC_HDR_SIZE );
1890                 
1891                 nk->subkeys.num_keys = nk->num_subkeys;
1892                 if (nk->subkeys.num_keys) {
1893                         if ( !(nk->subkeys.hashes = TALLOC_ZERO_ARRAY( file->mem_ctx, REGF_HASH_REC, nk->subkeys.num_keys )) )
1894                                 return NULL;
1895                 } else {
1896                         nk->subkeys.hashes = NULL;
1897                 }
1898                 nk->subkey_index = 0;
1899
1900                 /* update the max_bytes_subkey{name,classname} fields */
1901                 for ( i=0; i<nk->num_subkeys; i++ ) {
1902                         namelen = strlen( regsubkey_ctr_specific_key(subkeys, i) );
1903                         if ( namelen*2 > nk->max_bytes_subkeyname )
1904                                 nk->max_bytes_subkeyname = namelen * 2;
1905                 }
1906         }
1907
1908         /* write the values */
1909
1910         nk->values_off = REGF_OFFSET_NONE;
1911         if ( (nk->num_values = regval_ctr_numvals( values )) != 0 ) {
1912                 uint32 vlist_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8;
1913                 int i;
1914                 
1915                 if (!(vlist_hbin = find_free_space( file, vlist_size ))) {
1916                         return NULL;
1917                 }
1918                 nk->values_off = prs_offset( &vlist_hbin->ps ) + vlist_hbin->first_hbin_off - HBIN_HDR_SIZE;
1919         
1920                 if (nk->num_values) {
1921                         if ( !(nk->values = TALLOC_ARRAY( file->mem_ctx, REGF_VK_REC, nk->num_values )) )
1922                                 return NULL;
1923                 } else {
1924                         nk->values = NULL;
1925                 }
1926
1927                 /* create the vk records */
1928
1929                 for ( i=0; i<nk->num_values; i++ ) {
1930                         uint32 vk_size, namelen, datalen;
1931                         struct regval_blob *r;
1932
1933                         r = regval_ctr_specific_value( values, i );
1934                         create_vk_record( file, &nk->values[i], r );
1935                         vk_size = vk_record_data_size( &nk->values[i] );
1936                         nk->values[i].hbin = find_free_space( file, vk_size );
1937                         nk->values[i].hbin_off = prs_offset( &nk->values[i].hbin->ps );
1938                         nk->values[i].rec_size = ( vk_size - 1 ) ^ 0xFFFFFFFF;
1939                         nk->values[i].rec_off = prs_offset( &nk->values[i].hbin->ps ) 
1940                                 + nk->values[i].hbin->first_hbin_off 
1941                                 - HBIN_HDR_SIZE;
1942
1943                         /* update the max bytes fields if necessary */
1944
1945                         namelen = strlen( regval_name(r) );
1946                         if ( namelen*2 > nk->max_bytes_valuename )
1947                                 nk->max_bytes_valuename = namelen * 2;
1948
1949                         datalen = regval_size( r );
1950                         if ( datalen > nk->max_bytes_value )
1951                                 nk->max_bytes_value = datalen;
1952                 }
1953         }
1954
1955         /* stream the records */        
1956         
1957         prs_set_offset( &nk->hbin->ps, nk->hbin_off );
1958         if ( !prs_nk_rec( "nk_rec", &nk->hbin->ps, 0, nk ) )
1959                 return False;
1960
1961         if ( nk->num_values ) {
1962                 if ( !hbin_prs_vk_records( "vk_records", vlist_hbin, 0, nk, file ) )
1963                         return False;
1964         }
1965
1966
1967         regfio_flush( file );
1968
1969         return nk;
1970 }
1971