Index: trunk/include/byteorder.h
===================================================================
--- trunk/include/byteorder.h	(revision 30)
+++ trunk/include/byteorder.h	(revision 30)
@@ -0,0 +1,177 @@
+/* 
+ * Branched from Samba project Subversion repository, version #2:
+ *  http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h
+ *
+ * Unix SMB/CIFS implementation.
+ * SMB Byte handling
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-1998 Andrew Tridgell 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16 and int32
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
Index: trunk/include/regfio.h
===================================================================
--- trunk/include/regfio.h	(revision 30)
+++ trunk/include/regfio.h	(revision 30)
@@ -0,0 +1,235 @@
+/*
+ * Branched from Samba project, Subversion repository version #6903:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h
+ *
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/************************************************************
+ * Most of this information was obtained from 
+ * http://www.wednesday.demon.co.uk/dosreg.html
+ * Thanks Nigel!
+ ***********************************************************/
+
+#ifndef _REGFIO_H
+#define _REGFIO_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "smb_deps.h"
+
+
+/******************************************************************************/
+/* Macros */
+ 
+#define REGF_BLOCKSIZE		0x1000
+#define REGF_ALLOC_BLOCK	0x1000
+
+/* header sizes for various records */
+
+#define REGF_HDR_SIZE		4
+#define HBIN_HDR_SIZE		4
+#define HBIN_HEADER_REC_SIZE	0x24
+#define REC_HDR_SIZE		2
+
+#define REGF_OFFSET_NONE	0xffffffff
+
+/* Flags for the vk records */
+
+#define VK_FLAG_NAME_PRESENT	0x0001
+#define VK_DATA_IN_OFFSET	0x80000000
+
+/* NK record macros */
+
+#define NK_TYPE_LINKKEY		0x0010
+#define NK_TYPE_NORMALKEY	0x0020
+#define NK_TYPE_ROOTKEY		0x002c
+
+#define HBIN_STORE_REF(x, y)	{ x->hbin = y; y->ref_count++ };
+#define HBIN_REMOVE_REF(x, y)	{ x->hbin = NULL; y->ref_count-- /* if the count == 0; we can clean up */ };
+
+
+/* HBIN block */
+struct regf_hbin;
+typedef struct regf_hbin {
+	struct regf_hbin *prev, *next;
+	uint32 file_off;		/* my offset in the registry file */
+	uint32 free_off;		/* offset to free space within the hbin record */
+	uint32 free_size;		/* amount of data left in the block */
+	int ref_count;			/* how many active records are pointing to this block (not used currently) */
+	
+	char   header[HBIN_HDR_SIZE];	/* "hbin" */
+	uint32 first_hbin_off;		/* offset from first hbin block */
+	uint32 block_size;		/* block size of this blockually a multiple of 4096Kb) */
+
+	prs_struct ps;			/* data */
+
+	bool dirty;			/* has this hbin block been modified? */
+} REGF_HBIN;
+
+/* ??? List -- list of key offsets and hashed names for consistency */
+
+typedef struct {
+	uint32 nk_off;
+	uint8 keycheck[sizeof(uint32)];
+} REGF_HASH_REC;
+
+typedef struct {
+	REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing this nk record */
+	uint32 hbin_off;	/* offset from beginning of this hbin block */
+	uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+
+	char header[REC_HDR_SIZE];
+	uint16 num_keys;
+	REGF_HASH_REC *hashes;
+} REGF_LF_REC;
+
+/* Key Value */
+
+typedef struct {
+	REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing this nk record */
+	uint32 hbin_off;	/* offset from beginning of this hbin block */
+	uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+	uint32 rec_off;		/* offset stored in the value list */
+	
+	char header[REC_HDR_SIZE];
+	char *valuename;
+	uint32 data_size;
+	uint32 data_off;
+	uint8  *data;
+	uint32 type;
+	uint16 flag;
+} REGF_VK_REC;
+
+
+/* Key Security */
+struct _regf_sk_rec;
+
+typedef struct _regf_sk_rec {
+	struct _regf_sk_rec *next, *prev;
+	REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing this nk record */
+	uint32 hbin_off;	/* offset from beginning of this hbin block */
+	uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+
+	uint32 sk_off;		/* offset parsed from NK record used as a key
+				   to lookup reference to this SK record */
+
+	char header[REC_HDR_SIZE];
+	uint32 prev_sk_off;
+	uint32 next_sk_off;
+	uint32 ref_count;
+	uint32 size;
+	SEC_DESC *sec_desc;
+} REGF_SK_REC;
+
+/* Key Name */ 
+
+typedef struct {
+	REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing this nk record */
+	uint32 hbin_off;	/* offset from beginning of this hbin block */
+	uint32 subkey_index;	/* index to next subkey record to return */
+	uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+	
+	/* header information */
+	
+	char header[REC_HDR_SIZE];
+	uint16 key_type;
+	NTTIME mtime;
+	uint32 parent_off;	/* back pointer in registry hive */
+	uint32 classname_off;	
+	char *classname;
+	char *keyname;
+
+	/* max lengths */
+
+	uint32 max_bytes_subkeyname;		/* max subkey name * 2 */
+	uint32 max_bytes_subkeyclassname;	/* max subkey classname length (as if) */
+	uint32 max_bytes_valuename;		/* max valuename * 2 */
+	uint32 max_bytes_value;			/* max value data size */
+
+	/* unknowns */
+
+	uint32 unk_index;			/* nigel says run time index ? */
+	
+	/* children */
+	
+	uint32 num_subkeys;
+	uint32 subkeys_off;	/* hash records that point to NK records */	
+	uint32 num_values;
+	uint32 values_off;	/* value lists which point to VK records */
+	uint32 sk_off;		/* offset to SK record */
+	
+	/* link in the other records here */
+	
+	REGF_LF_REC subkeys;
+	REGF_VK_REC *values;
+	REGF_SK_REC *sec_desc;
+	
+} REGF_NK_REC;
+
+/* REGF block */
+ 
+typedef struct {
+	/* run time information */
+
+	int fd;				/* file descriptor */
+	int open_flags;			/* flags passed to the open() call */
+	void *mem_ctx;		/* memory context for run-time file access information */
+	REGF_HBIN *block_list;		/* list of open hbin blocks */
+
+	/* file format information */
+
+	char   header[REGF_HDR_SIZE];	/* "regf" */
+	uint32 data_offset;		/* offset to record in the first (or any?) hbin block */
+	uint32 last_block;		/* offset to last hbin block in file */
+	uint32 checksum;		/* XOR of bytes 0x0000 - 0x01FB */
+	NTTIME mtime;
+	
+	REGF_SK_REC *sec_desc_list;	/* list of security descriptors referenced by NK records */
+
+	/* unknowns used to simply writing */
+	
+	uint32 unknown1;
+	uint32 unknown2;
+	uint32 unknown3;
+	uint32 unknown4;
+	uint32 unknown5;
+	uint32 unknown6;
+	
+} REGF_FILE;
+
+/* Function Declarations */
+ 
+REGF_FILE*    regfio_open( const char *filename );
+int           regfio_close( REGF_FILE *r );
+
+REGF_NK_REC*  regfio_rootkey( REGF_FILE *file );
+REGF_NK_REC*  regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk );
+
+#endif	/* _REGFIO_H */
Index: trunk/include/smb_deps.h
===================================================================
--- trunk/include/smb_deps.h	(revision 30)
+++ trunk/include/smb_deps.h	(revision 30)
@@ -0,0 +1,452 @@
+/*
+ * This file contains miscellaneous pieces of code which regfio.c
+ * depends upon, from the Samba Subversion tree.  See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "byteorder.h"
+
+#define DEBUG(lvl,body) 0
+
+void* zalloc(size_t size);
+void* zcalloc(size_t size, unsigned int count);
+void zerop(void* p);
+
+
+/* From includes.h */
+
+#define uint8 unsigned char
+#define int16 short
+#define uint16 unsigned short
+#define int32 int
+#define uint32 unsigned int
+
+#define SMB_STRUCT_STAT struct stat
+#define QSORT_CAST (int (*)(const void *, const void *))
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+extern int DEBUGLEVEL;
+
+#define DLIST_ADD(list, p) \
+{ \
+        if (!(list)) { \
+		(list) = (p); \
+		(p)->next = (p)->prev = NULL; \
+	} else { \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(p)->prev = NULL; \
+		(list) = (p); \
+	}\
+}
+
+/* End of stuff from includes.h */
+
+/* From smb.h */
+
+#define MAXSUBAUTHS 15
+
+typedef struct sid_info
+{
+  uint8  sid_rev_num;             /**< SID revision number */
+  uint8  num_auths;               /**< Number of sub-authorities */
+  uint8  id_auth[6];              /**< Identifier Authority */
+  /*
+   *  Pointer to sub-authorities.
+   *
+   * @note The values in these uint32's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  /* '15' was previously the #define MAXSUBAUTHS */
+  uint32 sub_auths[15];
+
+} DOM_SID;
+
+typedef struct nttime_info
+{
+  uint32 low;
+  uint32 high;
+} NTTIME;
+
+/* End of stuff from smb.h */
+
+/* From smb_macros.h */
+
+#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define SMB_MALLOC_P(type) (type *)malloc_(sizeof(type))
+#define TALLOC_ARRAY(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+
+/* End of stuff from smb_macros.h */
+
+/* From ntdomain.h */
+
+struct uuid {
+       uint32 time_low;
+       uint16 time_mid;
+       uint16 time_hi_and_version;
+       uint8  clock_seq[2];
+       uint8  node[6];
+};
+
+typedef struct _prs_struct {
+	bool io; /* parsing in or out of data stream */
+	/* 
+	 * If the (incoming) data is big-endian. On output we are
+	  * always little-endian.
+	   */ 
+	   bool bigendian_data;
+	   uint8 align; /* data alignment */
+	   bool is_dynamic; /* Do we own this memory or not ? */
+	   uint32 data_offset; /* Current working offset into data. */
+	   uint32 buffer_size; /* Current allocated size of the buffer. */
+	   uint32 grow_size; /* size requested via prs_grow() calls */
+	   char *data_p; /* The buffer itself. */
+	   void *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define RPC_LITTLE_ENDIAN  0
+#define RPC_PARSE_ALIGN    4
+
+
+/* End of stuff from ntdomain.h */
+
+/* From nt_status.h */
+
+typedef uint32 NTSTATUS;
+typedef uint32 WERROR;
+
+/* End of stuff from nt_status.h */
+
+/* From lib/time.c */
+
+#define CHAR_BIT 8
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+void unix_to_nt_time(NTTIME *nt, time_t t);
+
+/* End of stuff from lib/time.c */
+
+/* From rpc_dce.h */
+
+#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
+
+/* End of stuff from rpc_dce.h */
+
+/* From parse_prs.h */
+
+bool prs_grow(prs_struct *ps, uint32 extra_space);
+bool prs_align(prs_struct *ps);
+bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io);
+char *prs_mem_get(prs_struct *ps, uint32 extra_size);
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32);
+bool prs_uint32s(bool charmode, const char *name, prs_struct *ps, 
+		 int depth, uint32 *data32s, int len);
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16);
+bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
+		    uint16 *data16, uint32 *offset);
+bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
+		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset);
+bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8);
+bool prs_uint8s(bool charmode, const char *name, prs_struct *ps, int depth, uint8 *data8s, int len);
+bool prs_set_offset(prs_struct *ps, uint32 offset);
+
+/* End of stuff from parse_prs.h */
+
+
+
+/* buffer used by \winreg\ calls to fill in arbitrary REG_XXX values.
+   It *may* look like a UNISTR2 but it is *not*.  This is not a goof
+   by the winreg developers.  It is a generic buffer.  buffer length
+   is stored in bytes (not # of uint16's) */
+
+typedef struct {
+	uint32 buf_max_len;
+	uint32 offset;
+	uint32 buf_len;
+	uint16 *buffer;
+} REGVAL_BUFFER;
+
+typedef struct {
+	uint32 buf_len;
+	uint16 *buffer; /* data */
+} BUFFER5;
+
+
+/********************************************************************** 
+ * UNICODE string variations
+ **********************************************************************/
+
+
+typedef struct {		/* UNISTR - unicode string size and buffer */
+	uint16 *buffer;		/* unicode characters. ***MUST*** be 
+				   little-endian. ***MUST*** be null-terminated */
+} UNISTR;
+
+typedef struct {		/* UNISTR2 - unicode string size (in 
+				   uint16 unicode chars) and buffer */
+	uint32 uni_max_len;
+	uint32 offset;
+	uint32 uni_str_len;
+	uint16 *buffer;		/* unicode characters. ***MUST*** be little-endian. 
+				  **must** be null-terminated and the uni_str_len 
+				  should include the NULL character */
+} UNISTR2;
+
+/* i think this is the same as a BUFFER5 used in the spoolss code --jerry */
+/* not sure about how the termination matches between the uint16 buffers thought */
+
+typedef struct {		/* UNISTR3 - XXXX not sure about this structure */
+	uint32 uni_str_len;
+	UNISTR str;
+} UNISTR3;
+
+typedef struct {		/* Buffer wrapped around a UNISTR2 */
+	uint16 length;		/* number of bytes not counting NULL terminatation */
+	uint16 size;		/* number of bytes including NULL terminatation */
+	UNISTR2 *string;
+} UNISTR4;
+
+typedef struct {
+	uint32 count;
+	UNISTR4 *strings;
+} UNISTR4_ARRAY;
+
+
+/********************************************************************** 
+ * String variations
+ **********************************************************************/
+
+typedef struct {		/* STRING2 - string size (in uint8 chars) and buffer */
+	uint32 str_max_len;
+	uint32 offset;
+	uint32 str_str_len;
+	uint8  *buffer; 	/* uint8 characters. **NOT** necessarily null-terminated */
+} STRING2;
+
+
+/* From rpc_secdesc.h */
+
+typedef struct security_info_info
+{
+	uint32 mask;
+
+} SEC_ACCESS;
+
+typedef struct security_ace_info
+{
+	uint8 type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+	uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+	uint16 size;
+
+	SEC_ACCESS info;
+
+	/* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+	uint32  obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+	struct uuid obj_guid;  /* object GUID */
+	struct uuid inh_guid;  /* inherited object GUID */		
+        /* eof object stuff */
+
+	DOM_SID trustee;
+
+} SEC_ACE;
+
+typedef struct security_acl_info
+{
+	uint16 revision; /* 0x0003 */
+	uint16 size; /* size in bytes of the entire ACL structure */
+	uint32 num_aces; /* number of Access Control Entries */
+
+	SEC_ACE *ace;
+
+} SEC_ACL;
+
+typedef struct security_descriptor_info
+{
+	uint16 revision; /* 0x0001 */
+	uint16 type;     /* SEC_DESC_xxxx flags */
+
+	uint32 off_owner_sid; /* offset to owner sid */
+	uint32 off_grp_sid  ; /* offset to group sid */
+	uint32 off_sacl     ; /* offset to system list of permissions */
+	uint32 off_dacl     ; /* offset to list of permissions */
+
+	SEC_ACL *dacl; /* user ACL */
+	SEC_ACL *sacl; /* system ACL */
+	DOM_SID *owner_sid; 
+	DOM_SID *grp_sid;
+
+} SEC_DESC;
+
+/* End of stuff from rpc_secdesc.h */
+
+
+/* From pstring.h */
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+/* End of stuff from pstring.h */
+
+/* From reg_objects.h */
+
+typedef struct _REGISTRY_VALUE {
+	fstring		valuename;
+	uint16		type;
+	/* this should be encapsulated in an RPC_DATA_BLOB */
+	uint32		size;	/* in bytes */
+	uint8           *data_p;
+} REGISTRY_VALUE;
+
+/* container for registry values */
+typedef struct {
+	void      *ctx;
+	uint32          num_values;
+	REGISTRY_VALUE	**values;
+} REGVAL_CTR;
+
+/* container for registry subkey names */
+typedef struct {
+	void	*ctx;
+	uint32          num_subkeys;
+	char            **subkeys;
+} REGSUBKEY_CTR;
+
+/* represent a registry key with all its subkeys and values */
+struct _regobj_key;
+
+typedef struct _regobj_key {
+	void *ctx;
+
+	char *name;
+
+	REGVAL_CTR values;
+	REGSUBKEY_CTR subkeys;
+} REGOBJ_KEY;
+
+/* End of stuff from reg_objects.h */
+
+/* From rpc_reg.h */
+
+/* Registry data types */
+
+#define REG_NONE                       0
+#define REG_SZ		               1
+#define REG_EXPAND_SZ                  2
+#define REG_BINARY 	               3
+#define REG_DWORD	               4
+#define REG_DWORD_LE	               4	/* DWORD, little endian */
+#define REG_DWORD_BE	               5	/* DWORD, big endian */
+#define REG_LINK                       6
+#define REG_MULTI_SZ  	               7
+#define REG_RESOURCE_LIST              8
+#define REG_FULL_RESOURCE_DESCRIPTOR   9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+
+/* End of stuff from rpc_reg.h */
+
+/* From rpc_secdes.h */
+
+#define SEC_DESC_DACL_PRESENT		0x0004
+#define SEC_DESC_SACL_PRESENT		0x0010
+#define  SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
+   /* thanks for Jim McDonough <jmcd@us.ibm.com> */
+#define SEC_ACE_OBJECT_PRESENT        0x00000001 
+#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+
+#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT	0x5
+#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT     	0x6
+#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT      	0x7
+#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT	0x8
+
+/* End of stuff from rpc_secdes.h */
+
+/* From rpc_parse/parse_misc.c */
+
+bool smb_io_uuid(const char *desc, struct uuid *uuid, 
+		 prs_struct *ps, int depth);
+bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth);
+bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth);
+
+/* End of stuff from rpc_parse/parse_misc.c */
+
+/* From lib/util_sid.c */
+
+size_t sid_size(const DOM_SID *sid);
+static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2);
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2);
+bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2);
+
+/* End of stuff from lib/util_sid.c */
+
+/* From lib/secace.c */
+
+bool sec_ace_object(uint8 type);
+
+/* End of stuff from lib/secace.c */
+
+/* From rpc_parse/parse_sec.c */
+
+bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth);
+bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth);
+bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth);
+bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth);
+
+/* End of stuff from rpc_parse/parse_sec.c */
+
+/* From lib/secace.c */
+
+bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2);
+
+/* End of stuff from lib/secace.c */
+
+/* From lib/secacl.c */
+
+bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2);
+
+/* End of stuff from lib/secacl.c */
+
+/* From lib/secdesc.c */
+
+bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2);
+
+/* End of stuff from lib/secdesc.c */
Index: trunk/lib/regfio.c
===================================================================
--- trunk/lib/regfio.c	(revision 30)
+++ trunk/lib/regfio.c	(revision 30)
@@ -0,0 +1,1257 @@
+/*
+ * Branched from Samba project Subversion repository, version #7470:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c
+ *
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+#include "../include/regfio.h"
+
+
+
+/*******************************************************************
+ *
+ * TODO : Right now this code basically ignores classnames.
+ *
+ ******************************************************************/
+
+
+/*******************************************************************
+*******************************************************************/
+static int read_block( REGF_FILE *file, prs_struct *ps, uint32 file_offset, 
+		       uint32 block_size )
+{
+	int bytes_read, returned;
+	char *buffer;
+	SMB_STRUCT_STAT sbuf;
+
+	/* check for end of file */
+
+	if ( fstat( file->fd, &sbuf ) ) {
+		DEBUG(0,("read_block: stat() failed! (%s)\n", strerror(errno)));
+		return -1;
+	}
+
+	if ( (size_t)file_offset >= sbuf.st_size )
+		return -1;
+	
+	/* if block_size == 0, we are parsnig HBIN records and need 
+	   to read some of the header to get the block_size from there */
+	   
+	if ( block_size == 0 ) {
+		uint8 hdr[0x20];
+
+		if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
+			DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));
+			return -1;
+		}
+
+		returned = read( file->fd, hdr, 0x20 );
+		if ( (returned == -1) || (returned < 0x20) ) {
+			DEBUG(0,("read_block: failed to read in HBIN header. Is the file corrupt?\n"));
+			return -1;
+		}
+
+		/* make sure this is an hbin header */
+
+		if ( strncmp( hdr, "hbin", HBIN_HDR_SIZE ) != 0 ) {
+			DEBUG(0,("read_block: invalid block header!\n"));
+			return -1;
+		}
+
+		block_size = IVAL( hdr, 0x08 );
+	}
+
+	DEBUG(10,("read_block: block_size == 0x%x\n", block_size ));
+
+	/* set the offset, initialize the buffer, and read the block from disk */
+
+	if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
+		DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));
+		return -1;
+	}
+	
+	prs_init( ps, block_size, file->mem_ctx, UNMARSHALL );
+	buffer = ps->data_p;
+	bytes_read = returned = 0;
+
+	while ( bytes_read < block_size ) {
+		if ( (returned = read( file->fd, buffer+bytes_read, block_size-bytes_read )) == -1 ) {
+			DEBUG(0,("read_block: read() failed (%s)\n", strerror(errno) ));
+			return false;
+		}
+		if ( (returned == 0) && (bytes_read < block_size) ) {
+			DEBUG(0,("read_block: not a vald registry file ?\n" ));
+			return false;
+		}	
+		
+		bytes_read += returned;
+	}
+	
+	return bytes_read;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool prs_regf_block( const char *desc, prs_struct *ps, int depth, REGF_FILE *file )
+{
+	depth++;
+	
+	if ( !prs_uint8s( true, "header", ps, depth, file->header, sizeof( file->header )) )
+		return false;
+	
+	/* yes, these values are always identical so store them only once */
+	
+	if ( !prs_uint32( "unknown1", ps, depth, &file->unknown1 ))
+		return false;
+	if ( !prs_uint32( "unknown1 (again)", ps, depth, &file->unknown1 ))
+		return false;
+
+	/* get the modtime */
+	
+	if ( !prs_set_offset( ps, 0x0c ) )
+		return false;
+	if ( !smb_io_time( "modtime", &file->mtime, ps, depth ) )
+		return false;
+
+	/* constants */
+	
+	if ( !prs_uint32( "unknown2", ps, depth, &file->unknown2 ))
+		return false;
+	if ( !prs_uint32( "unknown3", ps, depth, &file->unknown3 ))
+		return false;
+	if ( !prs_uint32( "unknown4", ps, depth, &file->unknown4 ))
+		return false;
+	if ( !prs_uint32( "unknown5", ps, depth, &file->unknown5 ))
+		return false;
+
+	/* get file offsets */
+	
+	if ( !prs_set_offset( ps, 0x24 ) )
+		return false;
+	if ( !prs_uint32( "data_offset", ps, depth, &file->data_offset ))
+		return false;
+	if ( !prs_uint32( "last_block", ps, depth, &file->last_block ))
+		return false;
+		
+	/* one more constant */
+	
+	if ( !prs_uint32( "unknown6", ps, depth, &file->unknown6 ))
+		return false;
+		
+	/* get the checksum */
+	
+	if ( !prs_set_offset( ps, 0x01fc ) )
+		return false;
+	if ( !prs_uint32( "checksum", ps, depth, &file->checksum ))
+		return false;
+	
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool prs_hbin_block( const char *desc, prs_struct *ps, int depth, REGF_HBIN *hbin )
+{
+	uint32 block_size2;
+
+	depth++;
+	
+	if ( !prs_uint8s( true, "header", ps, depth, hbin->header, sizeof( hbin->header )) )
+		return false;
+
+	if ( !prs_uint32( "first_hbin_off", ps, depth, &hbin->first_hbin_off ))
+		return false;
+
+	/* The dosreg.cpp comments say that the block size is at 0x1c.
+	   According to a WINXP NTUSER.dat file, this is wrong.  The block_size
+	   is at 0x08 */
+
+	if ( !prs_uint32( "block_size", ps, depth, &hbin->block_size ))
+		return false;
+
+	block_size2 = hbin->block_size;
+	prs_set_offset( ps, 0x1c );
+	if ( !prs_uint32( "block_size2", ps, depth, &block_size2 ))
+		return false;
+
+	if ( !ps->io )
+		hbin->dirty = true;
+	
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool prs_nk_rec( const char *desc, prs_struct *ps, int depth, REGF_NK_REC *nk )
+{
+	uint16 class_length, name_length;
+	uint32 start;
+	uint32 data_size, start_off, end_off;
+	uint32 unknown_off = REGF_OFFSET_NONE;
+
+	nk->hbin_off = ps->data_offset;
+	start = nk->hbin_off;
+	
+	depth++;
+	
+	/* back up and get the data_size */
+	
+	if ( !prs_set_offset( ps, ps->data_offset-sizeof(uint32)) )
+		return false;
+	start_off = ps->data_offset;
+	if ( !prs_uint32( "rec_size", ps, depth, &nk->rec_size ))
+		return false;
+	
+	if ( !prs_uint8s( true, "header", ps, depth, nk->header, sizeof( nk->header )) )
+		return false;
+		
+	if ( !prs_uint16( "key_type", ps, depth, &nk->key_type ))
+		return false;
+	if ( !smb_io_time( "mtime", &nk->mtime, ps, depth ))
+		return false;
+		
+	if ( !prs_set_offset( ps, start+0x0010 ) )
+		return false;
+	if ( !prs_uint32( "parent_off", ps, depth, &nk->parent_off ))
+		return false;
+	if ( !prs_uint32( "num_subkeys", ps, depth, &nk->num_subkeys ))
+		return false;
+		
+	if ( !prs_set_offset( ps, start+0x001c ) )
+		return false;
+	if ( !prs_uint32( "subkeys_off", ps, depth, &nk->subkeys_off ))
+		return false;
+	if ( !prs_uint32( "unknown_off", ps, depth, &unknown_off) )
+		return false;
+		
+	if ( !prs_set_offset( ps, start+0x0024 ) )
+		return false;
+	if ( !prs_uint32( "num_values", ps, depth, &nk->num_values ))
+		return false;
+	if ( !prs_uint32( "values_off", ps, depth, &nk->values_off ))
+		return false;
+	if ( !prs_uint32( "sk_off", ps, depth, &nk->sk_off ))
+		return false;
+	if ( !prs_uint32( "classname_off", ps, depth, &nk->classname_off ))
+		return false;
+
+	if ( !prs_uint32( "max_bytes_subkeyname", ps, depth, &nk->max_bytes_subkeyname))
+		return false;
+	if ( !prs_uint32( "max_bytes_subkeyclassname", ps, depth, &nk->max_bytes_subkeyclassname))
+		return false;
+	if ( !prs_uint32( "max_bytes_valuename", ps, depth, &nk->max_bytes_valuename))
+		return false;
+	if ( !prs_uint32( "max_bytes_value", ps, depth, &nk->max_bytes_value))
+		return false;
+	if ( !prs_uint32( "unknown index", ps, depth, &nk->unk_index))
+		return false;
+
+	name_length = nk->keyname ? strlen(nk->keyname) : 0 ;
+	class_length = nk->classname ? strlen(nk->classname) : 0 ;
+	if ( !prs_uint16( "name_length", ps, depth, &name_length ))
+		return false;
+	if ( !prs_uint16( "class_length", ps, depth, &class_length ))
+		return false;	
+		
+	if ( class_length ) {
+		;;
+	}
+	
+	if ( name_length ) {
+		if ( ps->io ) {
+		  if ( !(nk->keyname = (char*)zcalloc(sizeof(char), name_length+1 )) )
+				return false;
+		}
+
+		if ( !prs_uint8s( true, "name", ps, depth, nk->keyname, name_length) )
+			return false;
+
+		if ( ps->io ) 
+			nk->keyname[name_length] = '\0';
+	}
+
+	end_off = ps->data_offset;
+
+	/* data_size must be divisible by 8 and large enough to hold the original record */
+
+	data_size = ((start_off - end_off) & 0xfffffff8 );
+	if ( data_size > nk->rec_size )
+		DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, nk->rec_size));
+
+	if ( !ps->io )
+		nk->hbin->dirty = true;
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static uint32 regf_block_checksum( prs_struct *ps )
+{
+	char *buffer = ps->data_p;
+	uint32 checksum, x;
+	int i;
+
+	/* XOR of all bytes 0x0000 - 0x01FB */
+		
+	checksum = x = 0;
+	
+	for ( i=0; i<0x01FB; i+=4 ) {
+		x = IVAL(buffer, i );
+		checksum ^= x;
+	}
+	
+	return checksum;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool read_regf_block( REGF_FILE *file )
+{
+	prs_struct ps;
+	uint32 checksum;
+	
+	/* grab the first block from the file */
+		
+	if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) == -1 )
+		return false;
+	
+	/* parse the block and verify the checksum */
+	
+	if ( !prs_regf_block( "regf_header", &ps, 0, file ) )
+		return false;	
+		
+	checksum = regf_block_checksum( &ps );
+	
+        if(ps.is_dynamic)
+	  SAFE_FREE(ps.data_p);
+	ps.is_dynamic = false;
+	ps.buffer_size = 0;
+	ps.data_offset = 0;
+
+	if ( file->checksum !=  checksum ) {
+		DEBUG(0,("read_regf_block: invalid checksum\n" ));
+		return false;
+	}
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static REGF_HBIN* read_hbin_block( REGF_FILE *file, off_t offset )
+{
+	REGF_HBIN *hbin;
+	uint32 record_size, curr_off, block_size, header;
+	
+	if ( !(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN))) ) 
+		return NULL;
+	hbin->file_off = offset;
+	hbin->free_off = -1;
+		
+	if ( read_block( file, &hbin->ps, offset, 0 ) == -1 )
+		return NULL;
+	
+	if ( !prs_hbin_block( "hbin", &hbin->ps, 0, hbin ) )
+		return NULL;	
+
+	/* this should be the same thing as hbin->block_size but just in case */
+
+	block_size = hbin->ps.buffer_size;
+
+	/* Find the available free space offset.  Always at the end,
+	   so walk the record list and stop when you get to the end.
+	   The end is defined by a record header of 0xffffffff.  The 
+	   previous 4 bytes contains the amount of free space remaining 
+	   in the hbin block. */
+
+	/* remember that the record_size is in the 4 bytes preceeding the record itself */
+
+	if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE-sizeof(uint32) ) )
+		return false;
+
+	record_size = 0;
+	curr_off = hbin->ps.data_offset;
+	while ( header != 0xffffffff ) {
+		/* not done yet so reset the current offset to the 
+		   next record_size field */
+
+		curr_off = curr_off+record_size;
+
+		/* for some reason the record_size of the last record in
+		   an hbin block can extend past the end of the block
+		   even though the record fits within the remaining 
+		   space....aaarrrgggghhhhhh */
+
+		if ( curr_off >= block_size ) {
+			record_size = -1;
+			curr_off = -1;
+			break;
+		}
+
+		if ( !prs_set_offset( &hbin->ps, curr_off) )
+			return false;
+
+		if ( !prs_uint32( "rec_size", &hbin->ps, 0, &record_size ) )
+			return false;
+		if ( !prs_uint32( "header", &hbin->ps, 0, &header ) )
+			return false;
+		
+		assert( record_size != 0 );
+
+		if ( record_size & 0x80000000 ) {
+			/* absolute_value(record_size) */
+			record_size = (record_size ^ 0xffffffff) + 1;
+		}
+	}
+
+	/* save the free space offset */
+
+	if ( header == 0xffffffff ) {
+
+		/* account for the fact that the curr_off is 4 bytes behind the actual 
+		   record header */
+
+		hbin->free_off = curr_off + sizeof(uint32);
+		hbin->free_size = record_size;
+	}
+
+	DEBUG(10,("read_hbin_block: free space offset == 0x%x\n", hbin->free_off));
+
+	if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE )  )
+		return false;
+	
+	return hbin;
+}
+
+
+/*******************************************************************
+ Input a randon offset and receive the correpsonding HBIN 
+ block for it
+*******************************************************************/
+static bool hbin_contains_offset( REGF_HBIN *hbin, uint32 offset )
+{
+	if ( !hbin )
+		return false;
+	
+	if ( (offset > hbin->first_hbin_off) && (offset < (hbin->first_hbin_off+hbin->block_size)) )
+		return true;
+		
+	return false;
+}
+
+
+/*******************************************************************
+ Input a randon offset and receive the correpsonding HBIN 
+ block for it
+*******************************************************************/
+static REGF_HBIN* lookup_hbin_block( REGF_FILE *file, uint32 offset )
+{
+	REGF_HBIN *hbin = NULL;
+	uint32 block_off;
+
+	/* start with the open list */
+
+	for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
+		DEBUG(10,("lookup_hbin_block: address = 0x%x [0x%x]\n", hbin->file_off, (uint32)hbin ));
+		if ( hbin_contains_offset( hbin, offset ) )
+			return hbin;
+	}
+	
+	if ( !hbin ) {
+		/* start at the beginning */
+
+		block_off = REGF_BLOCKSIZE;
+		do {
+			/* cleanup before the next round */
+			if ( hbin )
+			{
+			  if(hbin->ps.is_dynamic)
+			    SAFE_FREE(hbin->ps.data_p);
+			  hbin->ps.is_dynamic = false;
+			  hbin->ps.buffer_size = 0;
+			  hbin->ps.data_offset = 0;
+			}
+
+			hbin = read_hbin_block( file, block_off );
+
+			if ( hbin ) 
+				block_off = hbin->file_off + hbin->block_size;
+
+		} while ( hbin && !hbin_contains_offset( hbin, offset ) );
+	}
+
+	if ( hbin )
+		DLIST_ADD( file->block_list, hbin );
+
+	return hbin;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool prs_hash_rec( const char *desc, prs_struct *ps, int depth, REGF_HASH_REC *hash )
+{
+	depth++;
+
+	if ( !prs_uint32( "nk_off", ps, depth, &hash->nk_off ))
+		return false;
+	if ( !prs_uint8s( true, "keycheck", ps, depth, hash->keycheck, sizeof( hash->keycheck )) )
+		return false;
+	
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool hbin_prs_lf_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk )
+{
+	int i;
+	REGF_LF_REC *lf = &nk->subkeys;
+	uint32 data_size, start_off, end_off;
+
+	depth++;
+
+	/* check if we have anything to do first */
+	
+	if ( nk->num_subkeys == 0 )
+		return true;
+
+	/* move to the LF record */
+
+	if ( !prs_set_offset( &hbin->ps, nk->subkeys_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
+		return false;
+
+	/* backup and get the data_size */
+	
+	if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
+		return false;
+	start_off = hbin->ps.data_offset;
+	if ( !prs_uint32( "rec_size", &hbin->ps, depth, &lf->rec_size ))
+		return false;
+
+	if ( !prs_uint8s( true, "header", &hbin->ps, depth, lf->header, sizeof( lf->header )) )
+		return false;
+		
+	if ( !prs_uint16( "num_keys", &hbin->ps, depth, &lf->num_keys))
+		return false;
+
+	if ( hbin->ps.io ) {
+	  if ( !(lf->hashes = (REGF_HASH_REC*)zcalloc(sizeof(REGF_HASH_REC), lf->num_keys )) )
+			return false;
+	}
+
+	for ( i=0; i<lf->num_keys; i++ ) {
+		if ( !prs_hash_rec( "hash_rec", &hbin->ps, depth, &lf->hashes[i] ) )
+			return false;
+	}
+
+	end_off = hbin->ps.data_offset;
+
+	/* data_size must be divisible by 8 and large enough to hold the original record */
+
+	data_size = ((start_off - end_off) & 0xfffffff8 );
+	if ( data_size > lf->rec_size )
+		DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, lf->rec_size));
+
+	if ( !hbin->ps.io )
+		hbin->dirty = true;
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk )
+{
+	prs_struct *ps = &hbin->ps;
+	uint16 tag = 0xFFFF;
+	uint32 data_size, start_off, end_off;
+
+
+	depth++;
+
+	if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
+		return false;
+
+	/* backup and get the data_size */
+	
+	if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
+		return false;
+	start_off = hbin->ps.data_offset;
+	if ( !prs_uint32( "rec_size", &hbin->ps, depth, &sk->rec_size ))
+		return false;
+
+	if ( !prs_uint8s( true, "header", ps, depth, sk->header, sizeof( sk->header )) )
+		return false;
+	if ( !prs_uint16( "tag", ps, depth, &tag))
+		return false;
+
+	if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off))
+		return false;
+	if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off))
+		return false;
+	if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count))
+		return false;
+	if ( !prs_uint32( "size", ps, depth, &sk->size))
+		return false;
+
+	if ( !sec_io_desc( "sec_desc", &sk->sec_desc, ps, depth )) 
+		return false;
+
+	end_off = hbin->ps.data_offset;
+
+	/* data_size must be divisible by 8 and large enough to hold the original record */
+
+	data_size = ((start_off - end_off) & 0xfffffff8 );
+	if ( data_size > sk->rec_size )
+		DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->rec_size));
+
+	if ( !hbin->ps.io )
+		hbin->dirty = true;
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool hbin_prs_vk_rec( const char *desc, REGF_HBIN *hbin, int depth, 
+			     REGF_VK_REC *vk, REGF_FILE *file )
+{
+	uint32 offset;
+	uint16 name_length;
+	prs_struct *ps = &hbin->ps;
+	uint32 data_size, start_off, end_off;
+
+	depth++;
+
+	/* backup and get the data_size */
+	
+	if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
+		return false;
+	start_off = hbin->ps.data_offset;
+	if ( !prs_uint32( "rec_size", &hbin->ps, depth, &vk->rec_size ))
+		return false;
+
+	if ( !prs_uint8s( true, "header", ps, depth, vk->header, sizeof( vk->header )) )
+		return false;
+
+	if ( !hbin->ps.io )
+		name_length = strlen(vk->valuename);
+
+	if ( !prs_uint16( "name_length", ps, depth, &name_length ))
+		return false;
+	if ( !prs_uint32( "data_size", ps, depth, &vk->data_size ))
+		return false;
+	if ( !prs_uint32( "data_off", ps, depth, &vk->data_off ))
+		return false;
+	if ( !prs_uint32( "type", ps, depth, &vk->type))
+		return false;
+	if ( !prs_uint16( "flag", ps, depth, &vk->flag))
+		return false;
+
+	offset = ps->data_offset;
+	offset += 2;	/* skip 2 bytes */
+	prs_set_offset( ps, offset );
+
+	/* get the name */
+
+	if ( vk->flag&VK_FLAG_NAME_PRESENT ) {
+
+		if ( hbin->ps.io ) {
+		  if ( !(vk->valuename = (char*)zcalloc(sizeof(char), name_length+1 )))
+				return false;
+		}
+		if ( !prs_uint8s( true, "name", ps, depth, vk->valuename, name_length ) )
+			return false;
+	}
+
+	end_off = hbin->ps.data_offset;
+
+	/* get the data if necessary */
+
+	if ( vk->data_size != 0 ) {
+		bool charmode = false;
+
+		if ( (vk->type == REG_SZ) || (vk->type == REG_MULTI_SZ) )
+			charmode = true;
+
+		/* the data is stored in the offset if the size <= 4 */
+
+		if ( !(vk->data_size & VK_DATA_IN_OFFSET) ) {
+			REGF_HBIN *hblock = hbin;
+			uint32 data_rec_size;
+
+			if ( hbin->ps.io ) {
+			  if ( !(vk->data = (uint8*)zcalloc(sizeof(uint8), vk->data_size) ) )
+					return false;
+			}
+
+			/* this data can be in another hbin */
+			if ( !hbin_contains_offset( hbin, vk->data_off ) ) {
+				if ( !(hblock = lookup_hbin_block( file, vk->data_off )) )
+					return false;
+			}
+			if ( !(prs_set_offset( &hblock->ps, (vk->data_off+HBIN_HDR_SIZE-hblock->first_hbin_off)-sizeof(uint32) )) )
+				return false;
+
+			if ( !hblock->ps.io ) {
+				data_rec_size = ( (vk->data_size+sizeof(uint32)) & 0xfffffff8 ) + 8;
+				data_rec_size = ( data_rec_size - 1 ) ^ 0xFFFFFFFF;
+			}
+			if ( !prs_uint32( "data_rec_size", &hblock->ps, depth, &data_rec_size ))
+				return false;
+			if ( !prs_uint8s( charmode, "data", &hblock->ps, depth, vk->data, vk->data_size) )
+				return false;
+
+			if ( !hblock->ps.io )
+				hblock->dirty = true;
+		}
+		else {
+		  if ( !(vk->data = zcalloc(sizeof(uint8), 4) ) )
+				return false;
+			SIVAL( vk->data, 0, vk->data_off );
+		}
+		
+	}
+
+	/* data_size must be divisible by 8 and large enough to hold the original record */
+
+	data_size = ((start_off - end_off ) & 0xfffffff8 );
+	if ( data_size !=  vk->rec_size )
+		DEBUG(10,("prs_vk_rec: data_size check failed (0x%x < 0x%x)\n", data_size, vk->rec_size));
+
+	if ( !hbin->ps.io )
+		hbin->dirty = true;
+
+	return true;
+}
+
+
+/*******************************************************************
+ read a VK record which is contained in the HBIN block stored 
+ in the prs_struct *ps.
+*******************************************************************/
+static bool hbin_prs_vk_records( const char *desc, REGF_HBIN *hbin, 
+				 int depth, REGF_NK_REC *nk, REGF_FILE *file )
+{
+	int i;
+	uint32 record_size;
+
+	depth++;
+	
+	/* check if we have anything to do first */
+	
+	if ( nk->num_values == 0 )
+		return true;
+		
+	if ( hbin->ps.io ) {
+	  if ( !(nk->values = (REGF_VK_REC*)zcalloc(sizeof(REGF_VK_REC), nk->num_values ) ) )
+			return false;
+	}
+	
+	/* convert the offset to something relative to this HBIN block */
+	
+	if ( !prs_set_offset( &hbin->ps, nk->values_off+HBIN_HDR_SIZE-hbin->first_hbin_off-sizeof(uint32)) )
+		return false;
+
+	if ( !hbin->ps.io ) { 
+		record_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8;
+		record_size = (record_size - 1) ^ 0xFFFFFFFF;
+	}
+
+	if ( !prs_uint32( "record_size", &hbin->ps, depth, &record_size ) )
+		return false;
+		
+	for ( i=0; i<nk->num_values; i++ ) {
+		if ( !prs_uint32( "vk_off", &hbin->ps, depth, &nk->values[i].rec_off ) )
+			return false;
+	}
+
+	for ( i=0; i<nk->num_values; i++ ) {
+		REGF_HBIN *sub_hbin = hbin;
+		uint32 new_offset;
+	
+		if ( !hbin_contains_offset( hbin, nk->values[i].rec_off ) ) {
+			sub_hbin = lookup_hbin_block( file, nk->values[i].rec_off );
+			if ( !sub_hbin ) {
+				DEBUG(0,("hbin_prs_vk_records: Failed to find HBIN block containing offset [0x%x]\n", 
+					nk->values[i].hbin_off));
+				return false;
+			}
+		}
+		
+		new_offset = nk->values[i].rec_off + HBIN_HDR_SIZE - sub_hbin->first_hbin_off;
+		if ( !prs_set_offset( &sub_hbin->ps, new_offset ) )
+			return false;
+		if ( !hbin_prs_vk_rec( "vk_rec", sub_hbin, depth, &nk->values[i], file ) )
+			return false;
+	}
+
+	if ( !hbin->ps.io )
+		hbin->dirty = true;
+
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_SK_REC* find_sk_record_by_offset( REGF_FILE *file, uint32 offset )
+{
+	REGF_SK_REC *p_sk;
+	
+	for ( p_sk=file->sec_desc_list; p_sk; p_sk=p_sk->next ) {
+		if ( p_sk->sk_off == offset ) 
+			return p_sk;
+	}
+	
+	return NULL;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_SK_REC* find_sk_record_by_sec_desc( REGF_FILE *file, SEC_DESC *sd )
+{
+	REGF_SK_REC *p;
+
+	for ( p=file->sec_desc_list; p; p=p->next ) {
+		if ( sec_desc_equal( p->sec_desc, sd ) )
+			return p;
+	}
+
+	/* failure */
+
+	return NULL;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool hbin_prs_key( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk )
+{
+	int depth = 0;
+	REGF_HBIN *sub_hbin;
+	
+	depth++;
+
+	/* get the initial nk record */
+	
+	if ( !prs_nk_rec( "nk_rec", &hbin->ps, depth, nk ))
+		return false;
+
+	/* fill in values */
+	
+	if ( nk->num_values && (nk->values_off!=REGF_OFFSET_NONE) ) {
+		sub_hbin = hbin;
+		if ( !hbin_contains_offset( hbin, nk->values_off ) ) {
+			sub_hbin = lookup_hbin_block( file, nk->values_off );
+			if ( !sub_hbin ) {
+				DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing value_list_offset [0x%x]\n", 
+					nk->values_off));
+				return false;
+			}
+		}
+		
+		if ( !hbin_prs_vk_records( "vk_rec", sub_hbin, depth, nk, file ))
+			return false;
+	}
+		
+	/* now get subkeys */
+	
+	if ( nk->num_subkeys && (nk->subkeys_off!=REGF_OFFSET_NONE) ) {
+		sub_hbin = hbin;
+		if ( !hbin_contains_offset( hbin, nk->subkeys_off ) ) {
+			sub_hbin = lookup_hbin_block( file, nk->subkeys_off );
+			if ( !sub_hbin ) {
+				DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing subkey_offset [0x%x]\n", 
+					nk->subkeys_off));
+				return false;
+			}
+		}
+		
+		if ( !hbin_prs_lf_records( "lf_rec", sub_hbin, depth, nk ))
+			return false;
+	}
+
+	/* get the to the security descriptor.  First look if we have already parsed it */
+	
+	if ( (nk->sk_off!=REGF_OFFSET_NONE) && !( nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )) ) {
+
+		sub_hbin = hbin;
+		if ( !hbin_contains_offset( hbin, nk->sk_off ) ) {
+			sub_hbin = lookup_hbin_block( file, nk->sk_off );
+			if ( !sub_hbin ) {
+				DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing sk_offset [0x%x]\n", 
+					nk->subkeys_off));
+				return false;
+			}
+		}
+		
+		if ( !(nk->sec_desc = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC) )) )
+			return false;
+		nk->sec_desc->sk_off = nk->sk_off;
+		if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc ))
+			return false;
+			
+		/* add to the list of security descriptors (ref_count has been read from the files) */
+
+		nk->sec_desc->sk_off = nk->sk_off;
+		DLIST_ADD( file->sec_desc_list, nk->sec_desc );
+	}
+		
+	return true;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob )
+{
+	char header[REC_HDR_SIZE] = "";
+	uint32 record_size;
+	uint32 curr_off, block_size;
+	bool found = false;
+	prs_struct *ps = &hbin->ps;
+	
+	curr_off = ps->data_offset;
+	if ( curr_off == 0 )
+		prs_set_offset( ps, HBIN_HEADER_REC_SIZE );
+
+	/* assume that the current offset is at the reacord header 
+	   and we need to backup to read the record size */
+
+	curr_off -= sizeof(uint32);
+
+	block_size = ps->buffer_size;
+	record_size = 0;
+	while ( !found ) {
+
+		curr_off = curr_off+record_size;
+		if ( curr_off >= block_size ) 
+			break;
+
+		if ( !prs_set_offset( &hbin->ps, curr_off) )
+			return false;
+
+		if ( !prs_uint32( "record_size", ps, 0, &record_size ) )
+			return false;
+		if ( !prs_uint8s( true, "header", ps, 0, header, REC_HDR_SIZE ) )
+			return false;
+
+		if ( record_size & 0x80000000 ) {
+			/* absolute_value(record_size) */
+			record_size = (record_size ^ 0xffffffff) + 1;
+		}
+
+		if ( memcmp( header, hdr, REC_HDR_SIZE ) == 0 ) {
+			found = true;
+			curr_off += sizeof(uint32);
+		}
+	} 
+
+	/* mark prs_struct as done ( at end ) if no more SK records */
+	/* mark end-of-block as true */
+	
+	if ( !found ) 
+	{
+	  prs_set_offset( &hbin->ps, hbin->ps.buffer_size );
+	  *eob = true;
+	  return false;
+	}
+		
+	if ( !prs_set_offset( ps, curr_off ) )
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static bool next_nk_record( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk, bool *eob )
+{
+	if ( next_record( hbin, "nk", eob ) && hbin_prs_key( file, hbin, nk ) )
+		return true;
+	
+	return false;
+}
+
+
+/*******************************************************************
+ Intialize the newly created REGF_BLOCK in *file and write the 
+ block header to disk 
+*******************************************************************/
+static bool init_regf_block( REGF_FILE *file )
+{	
+	prs_struct ps;
+	bool result = true;
+	
+	if ( !prs_init( &ps, REGF_BLOCKSIZE, file->mem_ctx, MARSHALL ) )
+		return false;
+		
+	memcpy( file->header, "regf", REGF_HDR_SIZE );
+	file->data_offset = 0x20;
+	file->last_block  = 0x1000;
+	
+	/* set mod time */
+	
+	unix_to_nt_time( &file->mtime, time(NULL) );
+	
+	/* hard coded values...no diea what these are ... maybe in time */
+	
+	file->unknown1 = 0x2;
+	file->unknown2 = 0x1;
+	file->unknown3 = 0x3;
+	file->unknown4 = 0x0;
+	file->unknown5 = 0x1;
+	file->unknown6 = 0x1;
+	
+	/* write header to the buffer */
+	
+	if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) {
+		result = false;
+		goto out;
+	}
+	
+	/* calculate the checksum, re-marshall data (to include the checksum) 
+	   and write to disk */
+	
+	file->checksum = regf_block_checksum( &ps );
+	prs_set_offset( &ps, 0 );
+	if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) {
+		result = false;
+		goto out;
+	}
+		
+out:
+        if(ps.is_dynamic)
+	  SAFE_FREE(ps.data_p);
+	ps.is_dynamic = false;
+	ps.buffer_size = 0;
+	ps.data_offset = 0;
+
+	return result;
+}
+
+
+/*******************************************************************
+ Open the registry file and then read in the REGF block to get the 
+ first hbin offset.
+*******************************************************************/
+REGF_FILE* regfio_open( const char *filename )
+{
+	REGF_FILE *rb;
+	int flags = O_RDONLY;
+
+	if ( !(rb = (REGF_FILE*)malloc(sizeof(REGF_FILE))) ) {
+		DEBUG(0,("ERROR allocating memory\n"));
+		return NULL;
+	}
+	zerop( rb );
+	rb->fd = -1;
+	
+	/*	if ( !(rb->mem_ctx = talloc_init( "read_regf_block" )) ) 
+	{
+	  regfio_close( rb );
+	  return NULL;
+	}
+	*/
+	rb->open_flags = flags;
+	
+	/* open and existing file */
+
+	if ( (rb->fd = open(filename, flags)) == -1 ) {
+		DEBUG(0,("regfio_open: failure to open %s (%s)\n", filename, strerror(errno)));
+		regfio_close( rb );
+		return NULL;
+	}
+	
+	/* check if we are creating a new file or overwriting an existing one */
+		
+	if ( flags & (O_CREAT|O_TRUNC) ) {
+		if ( !init_regf_block( rb ) ) {
+			DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));
+			regfio_close( rb );
+			return NULL;
+		}
+		
+		/* success */
+		return rb;
+	}
+	
+	/* read in an existing file */
+	
+	if ( !read_regf_block( rb ) ) {
+		DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));
+		regfio_close( rb );
+		return NULL;
+	}
+	
+	/* success */
+	
+	return rb;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+static void regfio_mem_free( REGF_FILE *file )
+{
+	/* free any zalloc()'d memory */
+	
+  /*	if ( file && file->mem_ctx )
+		free(file->mem_ctx);
+  */
+}
+
+
+/*******************************************************************
+*******************************************************************/
+int regfio_close( REGF_FILE *file )
+{
+	int fd;
+
+	regfio_mem_free( file );
+
+	/* nothing to do if there is no open file */
+
+	if ( !file || (file->fd == -1) )
+		return 0;
+		
+	fd = file->fd;
+	file->fd = -1;
+	SAFE_FREE( file );
+
+	return close( fd );
+}
+
+
+/*******************************************************************
+ There should be only *one* root key in the registry file based 
+ on my experience.  --jerry
+*******************************************************************/
+REGF_NK_REC* regfio_rootkey( REGF_FILE *file )
+{
+	REGF_NK_REC *nk;
+	REGF_HBIN   *hbin;
+	uint32      offset = REGF_BLOCKSIZE;
+	bool        found = false;
+	bool        eob;
+	
+	if ( !file )
+		return NULL;
+		
+	if ( !(nk = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC) )) ) {
+		DEBUG(0,("regfio_rootkey: zalloc() failed!\n"));
+		return NULL;
+	}
+	
+	/* scan through the file on HBIN block at a time looking 
+	   for an NK record with a type == 0x002c.
+	   Normally this is the first nk record in the first hbin 
+	   block (but I'm not assuming that for now) */
+	
+	while ( (hbin = read_hbin_block( file, offset )) ) {
+		eob = false;
+
+		while ( !eob) {
+			if ( next_nk_record( file, hbin, nk, &eob ) ) {
+				if ( nk->key_type == NK_TYPE_ROOTKEY ) {
+					found = true;
+					break;
+				}
+			}
+			if(hbin->ps.is_dynamic)
+			  SAFE_FREE(hbin->ps.data_p);
+			hbin->ps.is_dynamic = false;
+			hbin->ps.buffer_size = 0;
+			hbin->ps.data_offset = 0;
+		}
+		
+		if ( found ) 
+			break;
+
+		offset += hbin->block_size;
+	}
+	
+	if ( !found ) {
+		DEBUG(0,("regfio_rootkey: corrupt registry file ?  No root key record located\n"));
+		return NULL;
+	}
+
+	DLIST_ADD( file->block_list, hbin );
+
+	return nk;		
+}
+
+
+/*******************************************************************
+ This acts as an interator over the subkeys defined for a given 
+ NK record.  Remember that offsets are from the *first* HBIN block.
+*******************************************************************/
+REGF_NK_REC* regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk )
+{
+	REGF_NK_REC *subkey;
+	REGF_HBIN   *hbin;
+	uint32      nk_offset;
+
+	/* see if there is anything left to report */
+	
+	if ( !nk || (nk->subkeys_off==REGF_OFFSET_NONE) || (nk->subkey_index >= nk->num_subkeys) )
+		return NULL;
+
+	/* find the HBIN block which should contain the nk record */
+	
+	if ( !(hbin = lookup_hbin_block( file, nk->subkeys.hashes[nk->subkey_index].nk_off )) ) {
+		DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing offset [0x%x]\n", 
+			nk->subkeys.hashes[nk->subkey_index].nk_off));
+		return NULL;
+	}
+	
+	nk_offset = nk->subkeys.hashes[nk->subkey_index].nk_off;
+	if ( !prs_set_offset( &hbin->ps, (HBIN_HDR_SIZE + nk_offset - hbin->first_hbin_off) ) )
+		return NULL;
+		
+	nk->subkey_index++;
+	if (!(subkey = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC))))
+		return NULL;
+
+	if ( !hbin_prs_key( file, hbin, subkey ) )
+		return NULL;
+	
+	return subkey;
+}
Index: trunk/lib/smb_deps.c
===================================================================
--- trunk/lib/smb_deps.c	(revision 30)
+++ trunk/lib/smb_deps.c	(revision 30)
@@ -0,0 +1,1071 @@
+/*
+ * This file contains miscellaneous pieces of code which regfio.c
+ * depends upon, from the Samba Subversion tree.  See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include "../include/smb_deps.h"
+
+
+/* These act as replacements for numerous Samba memory allocation
+ *   functions. 
+ */
+void* zalloc(size_t size)
+{
+  void* ret_val = NULL;
+  if(ret_val = (void*)malloc(size))
+    memset(ret_val, 0, size);
+  return ret_val;
+}
+
+void* zcalloc(size_t size, unsigned int count)
+{
+  return zalloc(size*count);
+}
+
+void zerop(void* p)
+{
+  if(p)
+    memset((char*)p, 0, sizeof(*p));
+}
+
+
+/* From lib/time.c */
+
+/****************************************************************************
+ Put a 8 byte filetime from a time_t
+ This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void unix_to_nt_time(NTTIME *nt, time_t t)
+{
+	double d;
+
+	if (t==0) {
+		nt->low = 0;
+		nt->high = 0;
+		return;
+	}
+	if (t == TIME_T_MAX) {
+		nt->low = 0xffffffff;
+		nt->high = 0x7fffffff;
+		return;
+	}		
+	if (t == -1) {
+		nt->low = 0xffffffff;
+		nt->high = 0xffffffff;
+		return;
+	}		
+
+	/* this converts GMT to kludge-GMT */
+	/*XXX t -= TimeDiff(t) - get_serverzone(); */
+
+	d = (double)(t);
+	d += TIME_FIXUP_CONSTANT;
+	d *= 1.0e7;
+
+	nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+	nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+/* End of stuff from lib/time.c */
+
+/* From parse_prs.c */
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+bool prs_grow(prs_struct *ps, uint32 extra_space)
+{
+	uint32 new_size;
+	char *new_data;
+
+	ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+	if(ps->data_offset + extra_space <= ps->buffer_size)
+		return true;
+
+	/*
+	 * We cannot grow the buffer if we're not reading
+	 * into the prs_struct, or if we don't own the memory.
+	 */
+
+	if(ps->io || !ps->is_dynamic) {
+		return false;
+	}
+	
+	/*
+	 * Decide how much extra space we really need.
+	 */
+
+	extra_space -= (ps->buffer_size - ps->data_offset);
+	if(ps->buffer_size == 0) {
+		/*
+		 * Ensure we have at least a PDU's length, or extra_space, whichever
+		 * is greater.
+		 */
+
+		new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+
+		if((new_data = zalloc(new_size)) == NULL) {
+			return false;
+		}
+		memset(new_data, '\0', (size_t)new_size );
+	} else {
+		/*
+		 * If the current buffer size is bigger than the space needed, just 
+		 * double it, else add extra_space.
+		 */
+		new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);		
+
+		if ((new_data = (char*)realloc(ps->data_p, new_size)) == NULL) {
+			return false;
+		}
+
+		memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+	}
+	ps->buffer_size = new_size;
+	ps->data_p = new_data;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+bool prs_align(prs_struct *ps)
+{
+	uint32 mod = ps->data_offset & (ps->align-1);
+
+	if (ps->align != 0 && mod != 0) {
+		uint32 extra_space = (ps->align - mod);
+		if(!prs_grow(ps, extra_space))
+			return false;
+		memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+		ps->data_offset += extra_space;
+	}
+
+	return true;
+}
+
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size.  If >0, a new buffer will be
+ * created with malloc().
+ *
+ * @return false if allocation fails, otherwise true.
+ **/
+
+bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io)
+{
+	zerop(ps);
+	ps->io = io;
+	ps->bigendian_data = RPC_LITTLE_ENDIAN;
+	ps->align = RPC_PARSE_ALIGN;
+	ps->is_dynamic = false;
+	ps->data_offset = 0;
+	ps->buffer_size = 0;
+	ps->data_p = NULL;
+	ps->mem_ctx = ctx;
+
+	if (size != 0) {
+		ps->buffer_size = size;
+		if((ps->data_p = (char *)zalloc((size_t)size)) == NULL) {
+			return false;
+		}
+		memset(ps->data_p, '\0', (size_t)size);
+		ps->is_dynamic = true; /* We own this memory. */
+	}
+
+	return true;
+}
+
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+	if(ps->io) {
+		/*
+		 * If reading, ensure that we can read the requested size item.
+		 */
+		if (ps->data_offset + extra_size > ps->buffer_size) {
+			return NULL;
+		}
+	} else {
+		/*
+		 * Writing - grow the buffer if needed.
+		 */
+		if(!prs_grow(ps, extra_size))
+			return NULL;
+	}
+	return &ps->data_p[ps->data_offset];
+}
+
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+	char *q = prs_mem_get(ps, sizeof(uint32));
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		if (ps->bigendian_data)
+			*data32 = RIVAL(q,0);
+		else
+			*data32 = IVAL(q,0);
+	} else {
+		if (ps->bigendian_data)
+			RSIVAL(q,0,*data32);
+		else
+			SIVAL(q,0,*data32);
+	}
+
+	ps->data_offset += sizeof(uint32);
+
+	return true;
+}
+
+
+/******************************************************************
+ Stream an array of uint32s. Length is number of uint32s.
+ ********************************************************************/
+bool prs_uint32s(bool charmode, const char *name, prs_struct *ps, 
+		 int depth, uint32 *data32s, int len)
+{
+	int i;
+	char *q = prs_mem_get(ps, len * sizeof(uint32));
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		if (ps->bigendian_data) {
+			for (i = 0; i < len; i++)
+				data32s[i] = RIVAL(q, 4*i);
+		} else {
+			for (i = 0; i < len; i++)
+				data32s[i] = IVAL(q, 4*i);
+		}
+	} else {
+		if (ps->bigendian_data) {
+			for (i = 0; i < len; i++)
+				RSIVAL(q, 4*i, data32s[i]);
+		} else {
+			for (i = 0; i < len; i++)
+				SIVAL(q, 4*i, data32s[i]);
+		}
+	}
+
+	ps->data_offset += (len * sizeof(uint32));
+
+	return true;
+}
+
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
+{
+	char *q = prs_mem_get(ps, sizeof(uint16));
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		if (ps->bigendian_data)
+			*data16 = RSVAL(q,0);
+		else
+			*data16 = SVAL(q,0);
+	} else {
+		if (ps->bigendian_data)
+			RSSVAL(q,0,*data16);
+		else
+			SSVAL(q,0,*data16);
+	}
+
+
+	ps->data_offset += sizeof(uint16);
+
+	return true;
+}
+
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
+		    uint16 *data16, uint32 *offset)
+{
+	*offset = ps->data_offset;
+	if (ps->io) {
+		/* reading. */
+		return prs_uint16(name, ps, depth, data16);
+	} else {
+		char *q = prs_mem_get(ps, sizeof(uint16));
+		if(q ==NULL)
+			return false;
+		ps->data_offset += sizeof(uint16);
+	}
+	return true;
+}
+
+
+/*******************************************************************
+ prs_uint16 wrapper.  call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
+		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset)
+{
+	if (!ps->io) {
+		/* 
+		 * Writing - temporarily move the offset pointer.
+		 */
+		uint16 data_size = ps->data_offset - start_offset;
+		uint32 old_offset = ps->data_offset;
+
+		ps->data_offset = ptr_uint16;
+		if(!prs_uint16(name, ps, depth, &data_size)) {
+			ps->data_offset = old_offset;
+			return false;
+		}
+		ps->data_offset = old_offset;
+	} else {
+		ps->data_offset = start_offset + (uint32)(*data16);
+	}
+	return true;
+}
+
+
+/*******************************************************************
+ Stream a uint8.
+ ********************************************************************/
+bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
+{
+	char *q = prs_mem_get(ps, 1);
+	if (q == NULL)
+		return false;
+
+    if (ps->io)
+		*data8 = CVAL(q,0);
+	else
+		SCVAL(q,0,*data8);
+
+	ps->data_offset += 1;
+
+	return true;
+}
+
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+bool prs_uint8s(bool charmode, const char *name, prs_struct *ps, int depth, uint8 *data8s, int len)
+{
+	int i;
+	char *q = prs_mem_get(ps, len);
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		for (i = 0; i < len; i++)
+			data8s[i] = CVAL(q,i);
+	} else {
+		for (i = 0; i < len; i++)
+			SCVAL(q, i, data8s[i]);
+	}
+
+	ps->data_offset += len;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+bool prs_set_offset(prs_struct *ps, uint32 offset)
+{
+	if(offset <= ps->data_offset) {
+		ps->data_offset = offset;
+		return true;
+	}
+
+	if(!prs_grow(ps, offset - ps->data_offset))
+		return false;
+
+	ps->data_offset = offset;
+	return true;
+}
+
+/* End of stuff from parse_prs.c */
+
+/* From rpc_parse/parse_misc.c */
+
+/*******************************************************************
+ Reads or writes a struct uuid
+********************************************************************/
+bool smb_io_uuid(const char *desc, struct uuid *uuid, 
+		 prs_struct *ps, int depth)
+{
+	if (uuid == NULL)
+		return false;
+	depth++;
+
+	if(!prs_uint32 ("data   ", ps, depth, &uuid->time_low))
+		return false;
+	if(!prs_uint16 ("data   ", ps, depth, &uuid->time_mid))
+		return false;
+	if(!prs_uint16 ("data   ", ps, depth, &uuid->time_hi_and_version))
+		return false;
+
+	if(!prs_uint8s (false, "data   ", ps, depth, uuid->clock_seq, sizeof(uuid->clock_seq)))
+		return false;
+	if(!prs_uint8s (false, "data   ", ps, depth, uuid->node, sizeof(uuid->node)))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+	if (nttime == NULL)
+		return false;
+	depth++;
+
+	if(!prs_align(ps))
+		return false;
+	
+	if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+		return false;
+	if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a DOM_SID structure.
+********************************************************************/
+bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
+{
+	int i;
+
+	if (sid == NULL)
+		return false;
+	depth++;
+
+	if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
+		return false;
+
+	if(!prs_uint8 ("num_auths  ", ps, depth, &sid->num_auths))
+		return false;
+
+	for (i = 0; i < 6; i++)
+	{
+		fstring tmp;
+		snprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
+		if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
+			return false;
+	}
+
+	/* oops! XXXX should really issue a warning here... */
+	if (sid->num_auths > MAXSUBAUTHS)
+		sid->num_auths = MAXSUBAUTHS;
+
+	if(!prs_uint32s(false, "sub_auths ", ps, depth, sid->sub_auths, sid->num_auths))
+		return false;
+
+	return true;
+}
+
+/* End of stuff from rpc_parse/parse_misc.c */
+
+/* From lib/util_sid.c */
+
+/*****************************************************************
+ Calculates size of a sid.
+*****************************************************************/  
+size_t sid_size(const DOM_SID *sid)
+{
+	if (sid == NULL)
+		return 0;
+
+	return sid->num_auths * sizeof(uint32) + 8;
+}
+
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/  
+static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+	int i;
+
+	if (sid1 == sid2)
+		return 0;
+	if (!sid1)
+		return -1;
+	if (!sid2)
+		return 1;
+
+	if (sid1->sid_rev_num != sid2->sid_rev_num)
+		return sid1->sid_rev_num - sid2->sid_rev_num;
+
+	for (i = 0; i < 6; i++)
+		if (sid1->id_auth[i] != sid2->id_auth[i])
+			return sid1->id_auth[i] - sid2->id_auth[i];
+
+	return 0;
+}
+
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+	int i;
+
+	if (sid1 == sid2)
+		return 0;
+	if (!sid1)
+		return -1;
+	if (!sid2)
+		return 1;
+
+	/* Compare most likely different rids, first: i.e start at end */
+	if (sid1->num_auths != sid2->num_auths)
+		return sid1->num_auths - sid2->num_auths;
+
+	for (i = sid1->num_auths-1; i >= 0; --i)
+		if (sid1->sub_auths[i] != sid2->sub_auths[i])
+			return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+	return sid_compare_auth(sid1, sid2);
+}
+
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+	return sid_compare(sid1, sid2) == 0;
+}
+
+/* End of stuff from lib/util_sid.c */
+
+/* From lib/secace.c */
+
+/*******************************************************************
+ Check if ACE has OBJECT type.
+********************************************************************/
+
+bool sec_ace_object(uint8 type)
+{
+	if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+            type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+            type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+            type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
+		return true;
+	}
+	return false;
+}
+
+/* End of stuff from lib/secace.c */
+
+/* From rpc_parse/parse_sec.c */
+
+/*******************************************************************
+ Reads or writes a SEC_ACCESS structure.
+********************************************************************/
+bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
+{
+	if (t == NULL)
+		return false;
+
+	depth++;
+	
+	if(!prs_uint32("mask", ps, depth, &t->mask))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_ACE structure.
+********************************************************************/
+bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
+{
+	uint32 old_offset;
+	uint32 offset_ace_size;
+
+	if (psa == NULL)
+		return false;
+
+	depth++;
+	
+	old_offset = ps->data_offset;
+
+	if(!prs_uint8("type ", ps, depth, &psa->type))
+		return false;
+
+	if(!prs_uint8("flags", ps, depth, &psa->flags))
+		return false;
+
+	if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
+		return false;
+
+	if(!sec_io_access("info ", &psa->info, ps, depth))
+		return false;
+
+	/* check whether object access is present */
+	if (!sec_ace_object(psa->type)) {
+		if (!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+			return false;
+	} else {
+		if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
+			return false;
+
+		if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
+			if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
+				return false;
+
+		if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
+			if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
+				return false;
+
+		if(!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+			return false;
+	}
+
+	/* Theorectically an ACE can have a size greater than the
+	   sum of its components. When marshalling, pad with extra null bytes up to the
+	   correct size. */
+
+	if (!ps->io && (psa->size > ps->data_offset - old_offset)) {
+		uint32 extra_len = psa->size - (ps->data_offset - old_offset);
+		uint32 i;
+		uint8 c = 0;
+
+		for (i = 0; i < extra_len; i++) {
+			if (!prs_uint8("ace extra space", ps, depth, &c))
+				return false;
+		}
+	}
+
+	if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_ace_size, old_offset))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_ACL structure.  
+
+ First of the xx_io_xx functions that allocates its data structures
+ for you as it reads them.
+********************************************************************/
+bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
+{
+	unsigned int i;
+	uint32 old_offset;
+	uint32 offset_acl_size;
+	SEC_ACL *psa;
+
+	/*
+	 * Note that the size is always a multiple of 4 bytes due to the
+	 * nature of the data structure.  Therefore the prs_align() calls
+	 * have been removed as they through us off when doing two-layer
+	 * marshalling such as in the printing code (RPC_BUFFER).  --jerry
+	 */
+
+	if (ppsa == NULL)
+		return false;
+
+	psa = *ppsa;
+
+	if(ps->io && psa == NULL) {
+		/*
+		 * This is a read and we must allocate the stuct to read into.
+		 */
+	  if((psa = (SEC_ACL*)zalloc(sizeof(SEC_ACL))) == NULL)
+			return false;
+		*ppsa = psa;
+	}
+
+	depth++;
+	
+	old_offset = ps->data_offset;
+
+	if(!prs_uint16("revision", ps, depth, &psa->revision))
+		return false;
+
+	if(!prs_uint16_pre("size     ", ps, depth, &psa->size, &offset_acl_size))
+		return false;
+
+	if(!prs_uint32("num_aces ", ps, depth, &psa->num_aces))
+		return false;
+
+	if (ps->io) {
+		/*
+		 * Even if the num_aces is zero, allocate memory as there's a difference
+		 * between a non-present DACL (allow all access) and a DACL with no ACE's
+		 * (allow no access).
+		 */
+	  if((psa->ace = (SEC_ACE*)zcalloc(sizeof(SEC_ACE), psa->num_aces+1)) == NULL)
+			return false;
+	}
+
+	for (i = 0; i < psa->num_aces; i++) {
+		fstring tmp;
+		snprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
+		if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
+			return false;
+	}
+
+	/* Theorectically an ACL can have a size greater than the
+	   sum of its components. When marshalling, pad with extra null bytes up to the
+	   correct size. */
+
+	if (!ps->io && (psa->size > ps->data_offset - old_offset)) {
+		uint32 extra_len = psa->size - (ps->data_offset - old_offset);
+		uint8 c = 0;
+
+		for (i = 0; i < extra_len; i++) {
+			if (!prs_uint8("acl extra space", ps, depth, &c))
+				return false;
+		}
+	}
+
+	if(!prs_uint16_post("size     ", ps, depth, &psa->size, offset_acl_size, old_offset))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_DESC structure.
+ If reading and the *ppsd = NULL, allocates the structure.
+********************************************************************/
+bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
+{
+	uint32 old_offset;
+	uint32 max_offset = 0; /* after we're done, move offset to end */
+	uint32 tmp_offset = 0;
+
+	SEC_DESC *psd;
+
+	if (ppsd == NULL)
+		return false;
+
+	psd = *ppsd;
+
+	if (psd == NULL) {
+		if(ps->io) {
+		  if((psd = (SEC_DESC*)zalloc(sizeof(SEC_DESC))) == NULL)
+				return false;
+			*ppsd = psd;
+		} else {
+			/* Marshalling - just ignore. */
+			return true;
+		}
+	}
+
+	depth++;
+
+	/* start of security descriptor stored for back-calc offset purposes */
+	old_offset = ps->data_offset;
+
+	if(!prs_uint16("revision ", ps, depth, &psd->revision))
+		return false;
+
+	if(!prs_uint16("type     ", ps, depth, &psd->type))
+		return false;
+
+	if (!ps->io) {
+		uint32 offset = SEC_DESC_HEADER_SIZE;
+
+		/*
+		 * Work out the offsets here, as we write it out.
+		 */
+
+		if (psd->sacl != NULL) {
+			psd->off_sacl = offset;
+			offset += psd->sacl->size;
+		} else {
+			psd->off_sacl = 0;
+		}
+
+		if (psd->dacl != NULL) {
+			psd->off_dacl = offset;
+			offset += psd->dacl->size;
+		} else {
+			psd->off_dacl = 0;
+		}
+
+		if (psd->owner_sid != NULL) {
+			psd->off_owner_sid = offset;
+			offset += sid_size(psd->owner_sid);
+		} else {
+			psd->off_owner_sid = 0;
+		}
+
+		if (psd->grp_sid != NULL) {
+			psd->off_grp_sid = offset;
+			offset += sid_size(psd->grp_sid);
+		} else {
+			psd->off_grp_sid = 0;
+		}
+	}
+
+	if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid))
+		return false;
+
+	if(!prs_uint32("off_grp_sid  ", ps, depth, &psd->off_grp_sid))
+		return false;
+
+	if(!prs_uint32("off_sacl     ", ps, depth, &psd->off_sacl))
+		return false;
+
+	if(!prs_uint32("off_dacl     ", ps, depth, &psd->off_dacl))
+		return false;
+
+	max_offset = MAX(max_offset, ps->data_offset);
+
+	if (psd->off_owner_sid != 0) {
+
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
+			return false;
+
+		if (ps->io) {
+			/* reading */
+		  if((psd->owner_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
+				return false;
+		}
+
+		if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
+			return false;
+
+		max_offset = MAX(max_offset, ps->data_offset);
+
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if (psd->off_grp_sid != 0) {
+
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
+			return false;
+
+		if (ps->io) {
+			/* reading */
+			if((psd->grp_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
+				return false;
+		}
+
+		if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
+			return false;
+			
+		max_offset = MAX(max_offset, ps->data_offset);
+
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) {
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_sacl))
+			return false;
+		if(!sec_io_acl("sacl", &psd->sacl, ps, depth))
+			return false;
+		max_offset = MAX(max_offset, ps->data_offset);
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) {
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_dacl))
+			return false;
+		if(!sec_io_acl("dacl", &psd->dacl, ps, depth))
+			return false;
+		max_offset = MAX(max_offset, ps->data_offset);
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if(!prs_set_offset(ps, max_offset))
+		return false;
+
+	return true;
+}
+
+/* End of stuff from rpc_parse/parse_sec.c */
+
+/* From lib/secace.c */
+
+/*******************************************************************
+ Compares two SEC_ACE structures
+********************************************************************/
+bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
+{
+	/* Trivial case */
+
+	if (!s1 && !s2) return true;
+
+	/* Check top level stuff */
+
+	if (s1->type != s2->type || s1->flags != s2->flags ||
+	    s1->info.mask != s2->info.mask) {
+		return false;
+	}
+
+	/* Check SID */
+
+	if (!sid_equal(&s1->trustee, &s2->trustee)) {
+		return false;
+	}
+
+	return true;
+}
+
+/* End of stuff from lib/secace.c */
+
+/* From lib/secacl.c */
+
+/*******************************************************************
+ Compares two SEC_ACL structures
+********************************************************************/
+
+bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
+{
+	unsigned int i, j;
+
+	/* Trivial cases */
+
+	if (!s1 && !s2) return true;
+	if (!s1 || !s2) return false;
+
+	/* Check top level stuff */
+
+	if (s1->revision != s2->revision) {
+		return false;
+	}
+
+	if (s1->num_aces != s2->num_aces) {
+		return false;
+	}
+
+	/* The ACEs could be in any order so check each ACE in s1 against 
+	   each ACE in s2. */
+
+	for (i = 0; i < s1->num_aces; i++) {
+		bool found = false;
+
+		for (j = 0; j < s2->num_aces; j++) {
+			if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found) return false;
+	}
+
+	return true;
+}
+
+/* End of stuff from lib/secacl.c */
+
+/* From lib/secdesc.c */
+
+/*******************************************************************
+ Compares two SEC_DESC structures
+********************************************************************/
+bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
+{
+	/* Trivial case */
+
+	if (!s1 && !s2) {
+		goto done;
+	}
+
+	/* Check top level stuff */
+
+	if (s1->revision != s2->revision) {
+		return false;
+	}
+
+	if (s1->type!= s2->type) {
+		return false;
+	}
+
+	/* Check owner and group */
+
+	if (!sid_equal(s1->owner_sid, s2->owner_sid)) {
+		return false;
+	}
+
+	if (!sid_equal(s1->grp_sid, s2->grp_sid)) {
+		return false;
+	}
+
+	/* Check ACLs present in one but not the other */
+
+	if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+	    (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
+	{
+		return false;
+	}
+
+	/* Sigh - we have to do it the hard way by iterating over all
+	   the ACEs in the ACLs */
+
+	if (!sec_acl_equal(s1->dacl, s2->dacl) ||
+	    !sec_acl_equal(s1->sacl, s2->sacl)) 
+	{
+		return false;
+	}
+
+ done:
+	return true;
+}
+
+/* End of stuff from lib/secdesc.c */
Index: trunk/test/Makefile
===================================================================
--- trunk/test/Makefile	(revision 30)
+++ trunk/test/Makefile	(revision 30)
@@ -0,0 +1,19 @@
+# $Id$
+
+################################################################################
+
+REGFIO_O=../lib/regfio.o
+LIB_REGFIO_O=lib-regfio.o
+LIB_REGFIO=lib-regfio
+FILES=$(LIB_REGFIO) $(REGFIO_O)
+
+all: $(FILES)
+
+$(REGFIO_O): ../lib/regfio.c
+	$(CC) $(CFLAGS) $(OPTS) -c -o $@ ../lib/regfio.c
+
+$(LIB_REGFIO_O): lib-regfio.c
+	$(CC) $(CFLAGS) $(OPTS) -c -o $@ lib-regfio.c
+
+$(LIB_REGFIO): 	$(LIB_REGFIO_O) $(REGFIO_O)
+	$(CC) $(CFLAGS) $(OPTS) -o $@ $(LIB_REGFIO_O) $(REGFIO_O) ../lib/smb_deps.c
Index: trunk/test/lib-regfio.c
===================================================================
--- trunk/test/lib-regfio.c	(revision 30)
+++ trunk/test/lib-regfio.c	(revision 30)
@@ -0,0 +1,71 @@
+/*
+ * A utility to test functionality of Gerald Carter''s regio interface.
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "../include/regfio.h"
+
+void printKeyTree(REGF_FILE* f,  REGF_NK_REC* cur, char* prefix)
+{
+  REGF_NK_REC* sub;
+  char* sub_prefix;
+
+  if(prefix != NULL)
+  {
+    sub_prefix = (char*)zalloc(strlen(prefix)+strlen(cur->keyname)+2);
+    strcpy(sub_prefix, prefix);
+    strcat(sub_prefix, "/");
+  }
+  else
+    sub_prefix = (char*)zalloc(strlen(cur->keyname)+2);
+
+  strcat(sub_prefix, cur->keyname);
+
+  printf("%s:KEY\n", sub_prefix);
+  while ((sub = regfio_fetch_subkey(f, cur)) != NULL)
+    printKeyTree(f, sub, sub_prefix);
+
+  free(sub_prefix);
+}
+
+
+int main(int argc, char** argv)
+{
+  if(argc < 2)
+  {
+    printf("ERROR: Requires 1 argument.\n");
+    return 1;
+  }
+
+  REGF_FILE* f = regfio_open( argv[1] );
+  REGF_NK_REC* root = regfio_rootkey(f);
+
+  printKeyTree(f, root, NULL);
+  regfio_close(f);
+/*
+REGF_NK_REC*  regfio_rootkey( REGF_FILE *file );
+REGF_NK_REC*  regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk );
+*/
+
+  return 0;
+}
